Tytuł oryginału: CORE JAVASERVER FACES, Second Edition Tłumaczenie: Mikołaj Szczepaniak ISBN: 978-83-246-1354-0 Authorized translation from the Englis...
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
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
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
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
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" %> <04@ tag 11D uFi ="http //java Sun com/JS f/łUml" prefix="h" %> <( view>
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 28 29 30 31
s r
lilR liÉ lI [ 'J R SŁjT; ;:;i±... ..... ISII
Proszę wpisać «azwę i hasło użytkownika. Nazwa ‘ Hasie :
i
i ¿śfam 1
Zawartość tego pliku szczegółowo omówimy w dalszej części tego rozdziału, w punkcie „Strony technologii JS F ". Na razie ograniczymy się do następujących punktów: ■ Znaczna część użytych konstrukcji to standardowe znaczniki HTML-a, jak body czy Labie. ■ Dla niektórych znaczników dodatkowo użyto przedrostków , np. f •v i ew i h •mputText. Są to znaczniki technologii JS F . Dwie deklaracje Lagi ib na początku pliku deklarują biblioteki znaczników JSF . ■ Znaczniki h input Text, Lr inputSecret i h: cornmanclButton definiują odpowiednio pole tekstowe, pole hasła i przycisk akceptacji z rysunku 1.3.
Zakończono
W rzeczywistej aplikacji internetowej odpowiednia strona zostałaby oczywiście udekorowana dodatkowymi elementami opracowanymi przez utalentowanego grafika.
■ Pola tekstowe (w tym pole hasła) są powiązane z właściwościami odpowiedniego obiektu. Na przykład atrybut va 1ue="#{user nanie}" sygnalizuje implementacji JS F konieczność powiązania danego pola tekstowego z właściwością name obiektu user. Tego rodzaju powiązania zostaną szczegółowo omówione w punkcie „Komponenty” w dalszej części tego rozdziału.
20
JavaServer Faces
RozdziaM. ■ Wprowadzenie
Kiedy użytkownik wpisze nazwę i hasło, po czym kliknie przycisk Zaloguj, w oknie przeglą darki powinien się pojawić ekran powitalny (patrz rysunek 1.4). Kod źródłowy odpowiedniej strony zostanie przedstawiony na listingu 1.3 w dalszej części lego rozdziału. W punkcie „Naw igacja" wyjaśnimy, jak wygląda proces przechodzenia aplikacji pomiędzy ekranem logowania a ekranem powitalnym.
// Właściwość password: 12 pu blic S t r in g getPóSSw(Xd( ) | return password \ 13 public void setPassword(String newValue) { password = newValue, } 14 }
Struktura katalogów Komunikat powitalny zawiera podaną nazwę użytkownika. Na tym etapie ignorujemy wpisane hasło. Celem tej aplikacji oczywiście nie jest próba zaimponowania komukolwiek, tylko prezentacja rozmaitych elementów składowych niezbędnych do skonstruowania aplikacji w technologii JavaServer Faces.
Elementy składowe Nasza przykładowa aplikacja składa się z następujących elementów: ■ Stron definiujących ekran logowania i ekran powitalny. Odpowiednie strony nazwano indexjsp i welcomejsp.
M Komponentu (ang. bean) zarządzającego danymi użytkownika (w naszym przypadku jest to nazwa użytkownika i hasło). Komponent ma postać klasy Javy udostępniającej odpowiednie właściwości, które zwykle definiuje się zgodnie z prostymi konwencjami nazewniczymi dla metod zwracających i ustawiających. Kod tego komponentu zawarto w pliku UserBeanjava (patrz listing 1.2). Warto zwrócić uwagę na fakt przynależności tej klasy do pakietu com. corejsf. ■ Pliku konfiguracyjnego aplikacji, który opisuje zasoby komponentu i reguły nawigacji pomiędzy stronami. Domyślną nazwą tego pliku jest faces-config.xml. ■ Rozmaitych plików dodatkowych niezbędnych do właściwego funkcjonowania kontenera serwletów, czyli pliku web.xml i pliku index.html, który kieruje użytkownika pod adres U R L właściwy dla strony logowania. Bardziej zaawansowane aplikacje JS F charakteryzują się identyczną strukturą, tyle że zawie rają dodatkowe klasy Javy odpowiedzialne za obsługę zdarzeń, weryfikujące dane wejściowe i definiujące komponenty niestandardowe.
Aplikacja JavaServer Faces jest wdrażana w formie pliku W A R , czyli w istocie pliku Z IP z rozszerzeniem .war, który obejmuje strukturę katalogów zgodną z poniższym standardem. Oznacza to, że plik W A R skonstruowany dla naszej aplikacji przykładowej mógłby zawierać strukturę katalogów przedstawioną na rysunku 1.5. Jak widać, klasa UserBean należy do pakietu com. corejsf.
Rysunek 1.5. Struktura katalogów w ramach przykładowego pliku WAR
¡3 l o g i n . w a r f Q j
META-INF
' ...D
f-
MANIFEST.MF
w e b -inf
i j I I
i j ;
c las se s
j
r
I!
D
\ D
ł
3 com 9- £3 c o r e j s f ;
O
D
w e b .x m l in d e x, htm l
F D
in d e x .js p
;
w e tc o m e .js p
D
U s e r B e a n . class
fa c e s -c o n fig .x m l
Katalog META-INF jest tworzony automatycznie przez program jar w ramach procesu kon struowania pliku W A R . Kod źródłowy naszej aplikacji umieszczamy w nieco innej strukturze katalogów (zgodnie z konwencjami sformułowanymi w ramach projektu Java BluePrints — patrz strona interne towa http://java.sun.com/blueprints/code/projectconventions.html). Kod źródłowy znajduje się w katalogu src/java, natomiast strony JS F i pliki konfiguracyjne umieszczono w katalogu web (patrz rysunek 1.6).
22
Rozdziali. ■ Wprowadzenie
JavaServer Faces
W innych systemach możemy stanąć przed koniecznością dodania wielu plików JA R .
web D index.html D index.jsp D welcome.jsp r c a WEB-INF Q faces-config.xml Q web.xml
W systemie Windows należy użyć średników oddzielających poszczególne ścieżki: javac -classpath .g la s s fi sh \] ib\javaee.jar -d \ \web\WEB-1NF\classes com\corejsf\* java
Musimy się upewnić, że ścieżka do klas obejmuje bieżący katalog (reprezentowany przez kropkę).
Kompilacja przykładowej aplikacji W tym punkcie przeanalizujemy kolejne kroki składające się na proces ręcznej kompilacji aplikacji JavaServer Faces. Na końcu tego rozdziału omówimy sposób automatyzacji tego procesu. 1. Uruchom powłokę poleceń.
2. Przejdź do katalogu zawierającego przykładowy kod źródłowy dla tej książki. 3. Wykonaj następujące polecenia: cd chl/1ogin/src/java nikdir /. /web/WEB-INF/classes javac -d / /web/WEB-INF/classes com/corejsf/UserBean.java
W systemie Windows użyj lewych ukośników: cd chl\login\src\java nikdir ,\. .\web\WEB-INF\classes javac -d . A \web\WEB-INF\classes com\corejsf\UserBean.java
4. Przejdź do katalogu chi/login/web. 5. Wykonaj następujące polecenie:
Analiza przykładowej aplikacji Aplikacje internetowe składają się z dwóch podstawowych części: warstwy prezentacji i logiki biznesowej. Warstwa prezentacji odpowiada za wygląd aplikacji. W kontekście aplikacji wyświetlanych w oknie przeglądarki wygląd zależy oczywiście od znaczników HTM L-a opisujących układ, czcionki, obrazy itd. Logika biznesowa jest implementowana w formie kodu Javy definiującego zachowania aplikacji. Niektóre technologie internetowe wymagają mieszania kodu HTML-a z właściwym kodem źródłowym aplikacji. Taki model jest o tyle kuszący, że umożliwia zapisywanie prostych aplikacji w pojedynczych plikach. Z drugiej strony, w przypadku poważnych aplikacji mie szanie znaczników z kodem źródłowym prowadzi do poważnych utrudnień. Profesjonalni projektanci stron internetowych potrafią tworzyć dokumenty cechujące się doskonałym wyglądem, ale zwykle korzystają z narzędzi, które automatycznie tłumaczą ich wizje graficzne na kod HTML-a. Trudno oczekiwać, by chcieli mieć do czynienia z osadzonym kodem źródłowym. Z drugiej strony, typowy programista nie ma pojęcia o projektach graficznych (doskonałym przykładem są aplikacje opracowane dla tej książki właśnie przez programistów).
jar cvf login.war
(Warto zwrócić uwagę na użytą na końcu tego polecenia kropkę, która reprezentuje katalog bieżący). 6. Skopiuj plik login.w ar do katalogu glassfish/domains/domainl/autodeploy.
7. Upewnij się, że uruchomiono serwer GlassFish. W oknie przeglądarki wpisz adres: http://localhost:8080/1ogin
Oznacza to, że projektując profesjonalną aplikację internetową, należy oddzielić warstwę prezentacji od logiki biznesowej. Takie rozwiązanie umożliwia zarówno projektantom stron internetowych, jak i programistom implementującym właściwą logikę aplikacji koncentro wanie się wyłącznie na swoich zadaniach. W technologii JavaServer Faces kod aplikacji jest umieszczany w komponentach, natomiast projekt ma postać stron internetowych. W pierwszej kolejności zajmiemy się komponentami.
Ostatni krok powinien uruchomić naszą aplikację. Klasy komponentów w bardziej złożonych aplikacjach mogą wymagać interakcji z frameworkiem JSF . W takim przypadku krok kompilacji jest bardziej skomplikowany. Ścieżka do klas musi wówczas obejmować biblioteki JavaServer Faces. Jeśli korzystamy z serwera aplikacji GlassFish, powinniśmy dodać pojedynczy plik JA R : g7assf7's/7/lib/javaee. jar
Komponenty Komponent Javy (ang. Java bean) jest klasą udostępniającą właściwości i zdarzenia środo wisku zewnętrznemu, np. JSF . Właściwość jest wartością nazwaną określonego typu, która może być przedmiotem operacji odczytu i (lub) zapisu. Najprostszym sposobem definiowania
24
Rozdziali ■ Wprowadzenie
JavaServer Faces właściwości jest stosowanie standardowej konwencji nazewniczej dla metod odczytujących (zwracających) i zapisujących (ustawiających), czyli poprzedzanie nazw właściwości przed rostkami get i set. W każdym takim przypadku pierwszą literę nazwy właściwości należy zmienić z małej na wielką. Na przykład klasa UserBean zawiera dwie właściwości typu String nazwane name i password: p u b l ic c la s s UserBean { pub 1ic S t r i n g geiName() ] [ public void setNametS t r i n g newValue) { public S t r in g g etP a ssw o rd () \ f public void se tPasswo rcK Strmg newValue)
}
{
25
Kod interesującej nas strony rozpoczyna się od następujących deklaracji bibliotek znaczników: tag 1lb u r u " h t t p //javd Sun com /j sf/core" p r e f l x = " f tagl ib un=-"http //java sun.com/jsf/utnil" pre fix ="h "
%> %>
Implementacja JS F definiuje dwa zbiory znaczników. Znaczniki języka H TM L generują konstrukcje właściwe dla stron HTML-a. Gdybyśmy chcieli, aby nasza aplikacja intemelowa prezentowała strony w jakiejś alternatywnej technologii klienckiej, musielibyśmy użyć innej biblioteki znaczników. Znaczniki podstawowe (biblioteki core) są niezależne od technologii wizualizacji. I tak znacznik f ■view jest potrzebny zarówno dla stron H TM L, jak i dla stron prezentowanych na przykład na ekranie telefonu komórkowego.
f
Metody get i set mogą podejmować dowolne działania. W wielu przypadkach ich funkcjo nowanie ogranicza się do zwracania i ustawiania odpowiednich pól egzemplarzy. Tego rodzaju metody mogą jednak uzyskiwać dostęp do katalogów interfejsu JN D I (od ang. Java Naming
and Directory Interface). Zgodnie ze specyfikacją komponentów istnieje możliwość rezygnacji z metod odczytu i zapisu. Jeśli na przykład nie zdefiniujem y m etody getP assw ord, w łaściw ość password będzie dostępna tylko do zapisu. Takie rozwiązanie m oże być korzystne ze względów bezpieczeństwa. Z drugiej strony, technologia JSF nie do końca radzi sobie z podobnymi sytuacjami i w razie braku m etody odczytu lub zapisu generuje stosowne wyjątki (zam iast podejmować domyślne działania). Oznacza to, że najlepszym wyjściem je s t definiowanie odpowiednich m etod dla wszystkich właściwości kom ponentów. W aplikacjach JavaServer Faces wykorzystuje się komponenty dla wszystkich danych, które muszą być dostępne z poziomu stron internetowych. Komponenty pełnią funkcję swoistych łączników pomiędzy interfejsem użytkownika a wewnętrznymi mechanizmami aplikacji.
| Dla znaczników m ożna stosow ać dowolne przedrostki, np. fa c e s :view i h t m l: ^ in p u tT e x t. W tej książce będziem y się konsekw entnie posługiwali przedrost kiem f dla znaczników podstawowych i h dla znaczników HTML-a. Zdecydowana większość elementów opisywanej strony bardzo przypomina zwykły formularz języka H TM L. Warto jednak zwrócić uwagę na kilka różnic: ■ Wszystkie znaczniki technologii JS F zawierają się w znaczniku f •view. ■ Zamiast stosować znacznik f orni języka HTM L, umieszczono wszystkie komponenty JS F w znaczniku h : form. ■ Zamiast korzystać z popularnych znaczników input języka H TM L, korzystamy ze znaczników h. input Text, h- input Secret oraz h : commandBut ton. Wszystkie znaczniki standardowe technologii JS F wraz z ich atrybutami zostaną omówione w rozdziałach 4. i 5. W pierwszych trzech rozdziałach tej książki będziemy korzystali wyłącz nie z pól tekstowych i przycisków poleceń. Wartości pól tekstowych są wiązane z właściwościami komponentu nazwanego user:
Strony technologii JSF Potrzebujemy po jednej stronie JS F dla każdego ekranu prezentowanego w oknie przegłądarki. W zależności od środowiska programowania, w którym pracujemy, strony JS F z reguły mają postać plików z rozszerzeniami .jsp lub jsf. W czasie, kiedy pisano tę książkę, pliki z rozsze rzeniem .jsp wymagały mniej zabiegów konfiguracyjnych. Właśnie dlatego w prezentowanych przykładach będziemy się posługiwali rym rozszerzeniem. O ile pliki stron są oznaczane rozszerzeniami jsp lub jsf, o tyle zgodnie z zalecaną konfiguracją adresy URL stron powinny się kończyć rozszerzeniem .faces. Jeśli na przykład w przeglądarce internetowej wpiszemy adres URL http://localhost:8080/login/ index.faces, użyte rozszerzenie .faces zostanie odwzorowane w rozszerzenie jsp, po czym kontener serwletów załaduje plik indexjsp. Opisywany proces sprawia wra żenie niepotrzebnie zagm atw anego, jedn ak wynika wprost z koncepcji im plem entacji technologii JSF ponad technologią serwletów. Wróćmy raz jeszcze do pierwszej strony naszej przykładowej aplikacji, której kod przed stawiono na listingu 1.1.
Deklaracją zmiennej user zajmiemy się w punkcie „Nawigacja" w dalszej części tego podroz działu. Składnia separatorów #{ .. | zostanie wyjaśniona w podrozdziale „Składnia wyrażeń reprezentujących wartości" w rozdziale 2. W momencie wyświetlania interesującej nas strony framework wywołuje metodę get Name celem uzyskania bieżącej wartości odpowiedniej właściwości. W chwili akceptacji formularza (kliknięcia przycisku Zaloguj ) framework wywołuje metodę setName, aby ustawić wartość wpisaną przez użytkownika. Znacznik h: commandBut ton zawiera atrybut action, którego wartość jest wykorzystywana w procesie definiowania reguł nawigacji:
Reguły nawigacji zostaną omówione w punkcie „Nawigacja". Atrybut value reprezentuje łańcuch wyświetlany wewnątrz przycisku. Druga strona JS F naszej aplikacji jest jeszcze prostsza od pierwszej. Wykorzystano w niej znacznik h:outputText celem wyświetlenia wpisanej wcześniej (na stronie logowania) nazwy użytkownika (patrz listing 1.3).
26
Rozdziali. ■ Wprowadzenie
JavaServerFaces
Listing 1.3. Zawartość pliku I
2 3 4
^T,(a t a g l i b u n = ‘ htt p //java sun c om /j sf/core" prefix--"f t a g l i b u r 1 ="h 11 p //javd sun com/JS f / h t m l " prefix="h
5 6 /
<1 1 11e >Pros t a a p li k a c j d Ja v a S e r v e r F dces
8 9 10 11 12 13 14 15 16 17 18
witdmy w sw iec ie J a v a S e r v e r taces !
27
K E j Zdarza się, że mniej doświadczeni autorzy stron tworzą dokumenty rozpoczynające m M się od deklaracji DOCTYPE języka HTML w następującej form ie: taglib uri="http://java.sun.com/jsf/html” prefix="h" %> taglib uri="http://java.sun.com/jsf/core" prefix®’T ' %>
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:
Aby nasza pierwsza aplikacja JS F była kompletna, musimy jeszcze zdefiniować reguły na wigacji. Pojedyncza reguła nawigacji sygnalizuje stosowanej implementacji JS F , którą stronę należy odesłać do przeglądarki po akceptacji formularza na bieżącej stronie. W tym przypadku nawigacja jest bardzo prosta. Kiedy użytkownik kliknie przycisk Zaloguj, powinien zostać skierowany ze strony indexjsp na stronę welcomejsp. Opisywaną regułę można zdefiniować w pliku faees-eonfig.xml\ ^ n d v i g a t i o n ■r u l e > / in d e x . jsp< / f rom- v ipw- i(J> < n d v i g a t ion
cd se>
login n o -view -id>/weicome j sp < / n d v i g d t io n
case>
< / n d v i g d t i o n ■r u l e >
Wartość zdefiniowana w znaczniku f r o m - o u t c o m e odpowiada wartości atrybutu ściwego dla przycisku Zaloguj zdefiniowanego w kodzie strony indexjsp :
v d 1 u e = " Z d 1o g u j "
a ctio n
wła
a ctio n = "lo g in "/ >
Oprócz reguł nawigacji plik faees-eonfig.xml zawiera definicje komponentu. Poniżej przed stawiono definicję komponentu u s e r : u ser co m c o r e j s f
Czytelnicy korzystający z edytora przystosowanego do pracy na plikach w formacie XML koniecznie powinni rozważyć możliwość definiowania stron w tej form ie.
userBean< /m andg ed-bean -class>
s e s s i o n < / m a n a g e d ■b e a n ■scope >
Nazwę komponentu (w tym przypadku u s e r ) możemy wykorzystywać w atrybutach elemen tów interfejsu użytkownika. Na przykład kod strony indexjsp zawiera następujący znacznik:
v a l u e = " # { u s e r . n a m e } "/>
Atrybut value odnosi się do właściwości name komponentu user.
28
Rozdziali ■ Wprowadzenie
JavaServer Faces Znacznik managed-bean-class wskazuje na klasę komponentu (w tym przypadku com.corejsf. ^UserBean). I wreszcie znacznik managed-bean-scope reprezentuje zasięg — w tym przy padku session, ponieważ komponent ma obejmować swoim zasięgiem sesję. Oznacza to, że obiekt komponentu jest dostępny dla pojedynczego użytkownika niezależnie od aktualnie wyświetlanej strony. Różni użytkownicy korzystający z naszej aplikacji internetowej będą mieli przydzielane różne egzemplarze obiektu komponentu. Kompletny kod zawarty w pliku faces-config.xml przedstawiono na listingu 1.4.
Listing 1 A Zawartość pliku login/web/WEB-INF/faces-config.xml_______________________________________ 1. 2
http
6
v p n s i on=" 1 2 ">
7
/ / j a v a sun com/xml/ns/javaee/web- f a c e s c o n f i g _ l _ 2
xsd"
^ n a v ig a tio n - ru le^
8
< f r o m 'v i e w - i d > / 1 npex j s p < / f rorn-v i e w ■i d >
9
< n a v i g a t i o n case>
Konfiguracja serwletu Wdrażając aplikację JavaServer Faces na serwerze aplikacji, musimy dysponować plikiem konfiguracyjnym nazwanym web.xml. Szczęśliwie okazuje się, że ten sam plik web.xml można stosować dla większości tworzonych aplikacji JSF. Plik opracowany z myślą o naszej aplikacji przedstawiono na listingu 1.5.
v e r s i o n - "2 5"^ < s e r v l e t -name>Se^wlet Ja v a S e r v e r Faces ja va x faces webapp F ac e s S e rv le t< / s e rv le t - c la s s > l
10
< f rom•Outcome^1ogin
12
11
< t o ■v i e w ■i d>/we1come j s p < / to - v ie w - i d >
13 14 15 16 17
Senwlet Ja v a S e r v e r Face s< /s en vl et■name> < u n l ■pattern.>* faces
12 13
< / n a v i g a t i o n case>
14 15
16
user
18
17 18
com corejsf userBeansession
19
index
20
19 20
29
21
html
H H W technologii JSF 1 .2 składnię pliku konfiguracyjnego definiuje się w formie deklara■KaJ cji schem atu obejm ującego znaczniki konfiguracyjne: < /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 $ervlet-mapping>
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.
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
<|Łłocgit Z
Wizualne środowisko wytwarzania aplikacji JSF
*
Proszę wpisać nazwę i hasło użytkownika. N arw a taowak Hasło
s a s s s iiia i,?* t i f t y/eg fi « q « «
i
i
fer m
R«fsvtsip fetid FUgi CVS I w N
¥
pr- ^ «itjjggĘ
" Ociekiwamt- na iocaiho;
'«Sin
%
w eb Daqes $ ConfiqurdClon Files , jetvei Desources
n V* T* € ji®userBeaP.^ a | _____
Mfj j,, * | ^ ^ ^ ; I (~~ !ji |
ia i ~ iV yi a a
P
^ ń # L.1
UajS -
*
tan
p r i v a t e S t r in g name; p r iv a t e S t r in g passw ord;
; r e t u r n name;
:
;B
p u b li c S t r in g g et Password, r j I r e t u r n passw ord; p u b li c v o id s e t P a s s w o r d iS t r in g n ew Valuei
d is t
j1Local Variables
Name - #
*L
y~
rhis name
¡ j nU|l
# password
U nul'
variable■n^oimacion.sno- 3'-a-u
□
c o n n e c t -d en a g g ei
Attached JPPA genagaei
Rysunek 1.9.
<§* 8 j , CallStack
ivp
# L n o r e i a e n t a l l v d e p l o y i n g L o g in _ L a u s l h o s t 4 3 4 3 _ s e r v e r C o m p le te d m c r e m e m a J d i F t r i b u t i o n 01 l o g i n
iSPagel 1- iŚl Pagel 1 --i£lprm,i * P^ead! I ■^boovi
p u b li c c la s s U serBean i
core coreisf
c>if
;! c
-------
package co m .co ie q si ;
I SourcePackages ®
W ;nd*w
m 0
cr
Q
□ ; y !! i
' Localhost. 9009
Tnread LitpWorKerThreaa-ftOSO-O stopped at userBean.ia^a 9
Rysunek 1.7. Przykład użycia środowiska NetBeans w procesie diagnozowania aplikacji JSF
Harzędzia projektowania wizualnego Narzędzia projektowania wizualnego wyświetlają reprezentację graficzną komponentów i umożliwiają projektantom przeciąganie i upuszczanie komponentów dostępnych na palecie. Tego rodzaju narzędzia mogą mieć albo postać autonomicznych programów (jak Sun Java Studio Creator), albo postać modułów pracujących w ramach zintegrowanych środowisk wytwarzania.
33
Przykład automatycznie wygenerowanych znaczników JavaServer Faces
‘ł
9X
ti^fageł.jsp -±. ^
rn.me>
Mar.aqeaPcar.i +,^'“-ApDiiraricipBean ii ^"'Oeaues' Bean it ^'"SessionBean i '-fia Bourcef’acKagei Wuorana, .+ • DataBoufceOererer
Rysunek 1.10. Przykład wizualnego definiowania reguł nawigacji
iz j [Servers
ib 0 * # m i dietce-
<0 x
n r
y
# s
m
35
Czytelnicy, którzy zdecydują się korzystać z opracowanego przez nas skryptu, szczęśliwie nie muszą dysponować szczegółową wiedzą o narzędziu Ant. W pierwszej kolejności należy pobrać to narzędzie z witryny internetowej http://ant.apache.org i zainstalować je w wybranym katalogu. Użytkownicy serwera aplikacji GlassFish mogą skorzystać z narzędzia asant do stępnego w katalogu glassfis h/bin.
m m
qe V>. w o n •
}
i
r .r ,
i
Podczas kompilacji narzędzie Ant kieruje się zapisami zawartymi w pliku kompilacji. Domyślną nazwą tego pliku jest build.xml. Na potrzeby tego materiału opracowaliśmy plik build.xml umożliwiający kompilację aplikacji JSF . Wspomniany plik można znaleźć w ka talogu corejsf-examples. Nasz plik build.xml zawiera instrukcje opisujące sposób kompilacji, kopiowania, pakowania i wdrażania aplikacji na serwerze — całość w formie konstrukcji składniowych języka X M L (patrz listing 1.7).
Środowisko Java Studio Creator generuje strony, których struktura jest na tyle sztywna i niedoskonała, że trudno na ich podstawie opanowywać techniki programowania w tech nologii JS F . W związku z tym zalecamy korzystanie z innego środowiska podczas pracy z przykładami prezentowanymi w tej książce. Po przestudiowaniu tych przykładów wiedza Czytelnika o technologii JS F powinna być na tyle bogata, że wystarczy do efektywnego ko rzystania ze środowiska Java Studio Creator. Firma Sun zapowiada, że narzędzia projektowania wizualnego dostępne obecnie w ramach środowiska Java Studio Creator zostaną zintegrowane z przyszłymi wersjami środowiska Net Beans. Podobne funkcje mają być dostępne także w przyszłych wersjach środowiska Eclipse.
Automatyzacja procesu kompilacji za pomocą narzędzia Ant Wielu programistów jest na tyle mocno przywiązanych do swoich ulubionych edytorów tekstu lub środowisk ID E, że z góry odrzucają możliwość korzystania z innych narzędzi, nawet jeśli dotychczasowe oprogramowanie nie oferuje dostatecznej obsługi technologii JS F . Proces ręcznej kompilacji, który opisano we wcześniejszej części tego rozdziału, może być dość nużący, jeśli będzie wielokrotnie ponawiany. W tym punkcie omówimy sposób automatyzacji tego procesu z wykorzystaniem narzędzia Ant. Niniejszy materiał nie jest konieczny do pracy w technologii JavaServer Faces — jeśli wykorzystywane zintegrowane środowisko progra mowania oferuje dobrą obsługę technologii JS F lub jeśli ręczne kompilowanie aplikacji nie sprawia Czytelnikowi problemów, można bez obaw zrezygnować z lektury tego punktu.
compile: [javac] Compiling 1 source file to /home/cay/corejsf-examples/chl/login/buil d/WEB-INF/classes war:
^include name="**/* java"/>
fjarl Building jar: /home/cay/corejsf-examp Les/chl/logm/bui ld/login.war install: [copy} Copying 1 file to /usr/local/glassfish/domains/domainl/autodeploy
BUILD SUCCESSFUL Total time: 3 seconds ~/corejsf-examples$ _____________
58 59
^target name="war" depends="compile" d e s c n p t ion= "Budowanie p li k u wAR ">
60 61
62
63 64
65
66 67 68
w d r f 1 1e 1" t o d i r - " i { d e p l o y d i r } "
■Dapp=Ch1 / 1 o g l n
W tym konkretnym przypadku apache-ant jest katalogiem, w którym zainstalowano narzędzie Ant (np. e:\apache-ant-l.6.5). Użytkownicy serwera GlassFish mogą się posłużyć poleceniem: g/assf/s/i/bi n/a san t
Dapp^cnl/ login
72 73 74
Otwórz powlokę poleceń i przejdź do katalogu corejsf-examples.
d P â C h e - â n t /D in/ant
">
71
1.
2. Wykonaj polecenie:
69 70
i nit: prepare: [rakdir] Created dir: /home/cay/corejsf-examples/chl/login/build
[copy] Copying 5 files to /horae/cay/corejsf-exainples/chl/login/build [copy] Copied 4 empty directories to 3 empty directories under /horae/cay/co rej sf-examples/chl/login/build
51. 52.
55
EHe fitfit View lermirtai Tafes Help
~/corejsf~examples$ ant -Dapp=chl/login Buildfile: build.xml
copy:
48
56 57
Instalacja aplikacji internetowej z wykorzystaniem narzędzia Ant
37
Aby korzystać z tego pliku kompilacji, musimy odpowiednio zmienić zawartość pliku
build.propenies składowanego w tym samym katalogu. Domyślną zawartość lego pliku przedstawiono na listingu 1.8.
ffS 9j Prezentowany skrypt języka Ant nieznacznie różni się od skryptów, które z reguły « 1 są dostarczane wraz z rozm aitym i aplikacjam i przykładowym i. Za pom ocą tego jedn ego skryptu m ożna kom pilow ać wszystkie aplikacje opracow ane dia tej książki. Nazwa kompilowanej aplikacji je s t reprezentowana przez flagę -Dapp= Wydaje się, że takie podejście je s t lepsze niż każdorazowe opracowywanie niemal identycznych skryp tów. Warto też podkreślić, że nasz skrypt nie jest wywoływany z poziomu katalogu aplika cji, tylko katalogu corejsf~examples.
Listing 1,8. Plik build.properties________________________________________________________________________ 1 appserver d m $ | e n v GLASSF!SH_hOME} 2
javaee api jar =4{appser ver d i r } / ] ib/java ee j a r
3
depioy dir -$|ap p se rv e r dir}/domams/domain 1/autodep 1oy
Instalacja lokalna na serwerze aplikacji wymaga zmodyfikowania katalogu docelowego. W tym celu należy odpowiednio zmienić pierwszy wiersz w pliku build.properties. Możemy teraz przystąpić do właściwej kompilacji naszej przykładowej aplikacji (patrz ry sunek 1.11).
Usługi frameworka JSF Skoro dokonaliśmy już analizy naszej pierwszej aplikacji JavaServer Faces, wyjaśnienie usług oferowanych programiście przez framework JS F powinno być dużo prostsze. Ogólną archi tekturę tej technologii przedstawiono na rysunku 1.12. Jak widać, framework JS F odpowiada za interakcję z urządzeniami klienckimi i oferuje narzędzia niezbędne do łączenia w jedną całość prezentacji graficznej, logiki aplikacji i logiki biznesowej aplikacji internetowej. Funk cjonalność tego frameworku kończy się jednak na warstwie prezentacji — obsługa utrwalania obiektów w bazie danych, usług sieciowych i połączeń wewnętrznych wykracza poza moż liwości tej technologii.
38
Rozdziali. ■ Wprowadzenie
JavaServer Faces
39
Weryfikacja danych i obsługa błędów — technologia JS F ułatwia definiowanie
Poniżej wymieniono i krótko opisano najważniejsze usługi frameworku JSF :
Architektura model-widok-kontroler — wszystkie aplikacje umożliwiają użytkownikom operowanie na pewnych danych, np. koszykach zamówień, planach podróży lub dowolnych innych danych właściwych dla danej dziedziny problemu. Wszystkie te dane określamy mianem modelu. Podobnie jak artysta malarz tworzy obraz, obserwując modelkę w swoim studiu, tak programista aplikacji JS F tworzy widoki na podstawie danych modelu. W aplikacjach internetowych widoki są „malowane” w języku H T M L (lub innej, podobnej technologii wizualizacji). Technologia JS F wiąże widok z modelem. Mieliśm y już okazję się przekonać, że komponent widoku może się odwoływać do właściwości obiektu modelu:
reguł weryfikacji danych dla pól tekstowych (w tym pól wymaganych i pól, w których użytkownicy powinni wpisywać wartości liczbowe). W odpowiedzi na wpisanie błędnych danych nasza aplikacja powinna oczywiście wyświetlić stosowny komunikat o błędzie. Oznacza to, że framework JavaServer Faces zwalnia programistę z obowiązku samodzielnej implementacji tych elementów. Problemem weryfikacji danych zajmiemy się w rozdziale 6.
Umiędzynarodowienie — framework JavaServer Faces zarządza takimi elementami umiędzynarodawiania aplikacji jak kodowanie znaków czy dobór właściwych pakietów zasobów. Problem wyboru pakietów zostanie omówiony w podrozdziale „Pakiety komunikatów” w rozdziale 2. Komponenty niestandardowe — programiści komponentów mogą tworzyć własne, często bardzo wyszukane komponenty, które projektanci stron mogą następnie umieszczać na tworzonych stronach. Przypuśćmy na przykład, że programista komponentów opracował komponent kalendarza ze wszystkimi niezbędnymi elementami graficznymi i funkcjonalnymi. Takiego komponentu można by użyć na stronie, stosując następujące polecenie:
Szczegółowe omówienie zagadnienia tworzenia komponentów niestandardowych można znaleźć w rozdziale 9.
Alternatywne metody wizualizacji — framework JS F domyślnie generuje znaczniki dla stron H TM L. Okazuje się jednak, że można ten framework bez trudu rozbudować o mechanizmy generujące znaczniki innych języków opisu stron, w tym języków W M L i X U L .
Co więcej, framework JS F pełni funkcję kontrolera reagującego na działania podejmowane przez użytkownika, przetwarzając akcje i zdarzenia zmian wartości oraz kierując je do kodu odpowiedzialnego za aktualizację modelu lub widoku. Możemy na przykład zaimplementować rozwiązanie polegające na wywoływaniu odpowiedniej metody za każdym razem, gdy użytkownik może się zalogować. W tym celu wystarczy użyć następującego znacznika JS F :
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\ usercom.corejsf.UserBeansession
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
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 messages <'var>msgs
Dla tych samych ustawień regionalnych może istnieć wiele pakietów komunikatów. Możemy na przykład opracować odrębne pakiety dla najczęściej wykorzystywanych komunikatów o błędach.
Alternatywnym rozwiązaniem jest dodanie elementu f ■1oacIBunct 1e do każdej strony JavaServer Faces wymagającej dostępu do danego pakietu komunikatów:
Komunikaty obejmujące zmienne
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).
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.
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
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.
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(
ti tle=Zahlenqui z heading=Viel Spa\uOOdf mit dem Zahlenquiz! currentScore=Sie haben (0,choice,0#0 Punkte|l#einen Punkt|2#{0} Punkte} guessNext=Raten Sie die n\u00e4chste Zahl in der Folge! answer=Ihre Antwort. next=Weiter
59
60
Rozdział 2. n Komponenty zarządzane
JavaServer Faces
Komponenty wspierające W niektórych sytuacjach najwygodniejszym rozwiązaniem jest zaprojektowanie kompo nentu obejmującego wybraną część lub wszystkie obiekty komponentów właściwe dla danego formularza W W W . Taki komponent określa się mianem komponentu wspierającego, wspo magającego (ang. backing bean). Możemy zdefiniować komponent wspierający dla formularza quizu przez dodanie odpo wiednich właściwości do komponentu tego formularza: public class QuizFormBean { private UlOutput scoreComponent, private UIInput answerComponent; // Właściwość scoreComponent: public UIQutput getScoreComponent() { return scoreComponent. } public void setScoreComponentdJIOutput newValue) { scoreComponent = newValue, } // Właściwość answerComponent: public UIInput getAnswerComponentO { return answerComponent. } public void setAnswerComponent(UIInput newValue) { answerComponent = newValue. }
} Komponenty wyjściowe interfejsu użytkownika należą do klasy UlOutput, natomiast kom ponenty wejściowe należą do klasy UI Input. Obie te klasy szczegółowo omówimy w punkcie „Zestaw narzędzi programisty komponentów niestandardowych” w rozdziale 9. Po co właściwie mielibyśmy tworzyć tego rodzaju komponenty? Podczas lektury punktu „W eryfikacja relacji łączących wiele komponentów” w rozdziale 6. Czytelnicy dowiedzą się, że w niektórych przypadkach mechanizmy weryfikujące i obsługujące zdarzenia muszą mieć dostęp do komponentów interfejsu użytkownika na formularzu. Co więcej, komponenty wspierające (w środowisku Java Studio Creator nazywane komponentami stron, ang. page beans, z uwagi na konieczność ich dodawania do wszystkich stron) z reguły są wykorzystywa ne przez wizualne środowiska wytwarzania aplikacji JS F . Wspomniane środowiska auto matycznie generują odpowiednie metody zwracające i ustawiające wartości właściwości dla wszystkich komponentów interfejsu graficznego przeciąganych na formularz. Jeśli zdecydujemy się na użycie komponentu wspierającego, będziemy musieli związać komponenty interfejsu użytkownika dodane do formularza z komponentami wchodzącymi w skład komponentu wspierającego. Możemy zrealizować ten cel, stosując atrybut binding:
61
Jeśli komponenty wspierające wykorzystujemy dla danych warstwy prezentacji, dla obiektów biznesowych powinniśmy stosować odrębny zbiór komponentów.
Zasięg komponentów Z myślą o zapewnieniu wygody programistom aplikacji internetowych kontenery serwletów oferują odrębne zasięgi, z których każdy zarządza tabelą związków nazwa-wartość. Każdy z tych zasięgów zwykle obejmuje komponenty i inne obiekty, które muszą być dostępne z poziomu innych komponentów aplikacji internetowej.
Komponenty obejmujące zasięgiem sesję Musimy pamiętać, że protokół HTTP jest bezstanowy. Przeglądarka wysyła żądanie na serwer, serwer zwraca odpowiedź, po czym żadna ze stron (ani przeglądarka, ani serwer) nie ma obowiązku utrzymywać w pamięci jakichkolwiek informacji o tej transakcji. Taki model zdaje egzamin w sytuacji, gdy przeglądarka żąda od serwera prostych informacji, ale okazuje się dalece niedoskonały w przypadku aplikacji pracujących po stronie serwera. Na przykład w aplikacji sklepu internetowego oczekujemy od serwera zachowywania zawartości koszyka z zakupami. Właśnie dlatego kontenery serwletów rozszerzają protokół HTTP o mechanizm utrzymywania i śledzenia sesji , czyli następujących po sobie połączeń nawiązywanych przez tego samego klienta. Istnieje wiele różnych metod śledzenia sesji. Najprostszą z nich jest stosowanie znaczników kontekstu klienta (tzw. ciasteczek; ang. cookies) — par nazwa-wartość wysy łanych klientowi przez serwer w założeniu, że będą zwracane w ramach kolejnych żądań (patrz rysunek 2.6).
Rysunek 2.6. Znacznik kontekstu klienta wysłany przez aplikację JSF
..
U l Ciasteczka
SzuKd) ! Na tym tom p u tętze przechow yw ane są •'>astępu1actr ctasceczica
Nazwa cia^teczla
N a w a : ‘.SESSION®
Zawartość: c 2 1 4 5 b2 2 d d b6b h a ia 4f3 ?5 6 60d 40 Host: tocałhost Ścieżka; /lo g io
Po skonstruowaniu drzewa komponentów dla danego formularza następuje wywołanie metody getSconeComponent komponentu wspomagającego, która jednak zwraca wartość nul 1. Oznacza
to, że komponent wyjściowy jest konstruowany i instalowany w ramach tego komponentu wspierającego za pomocą metody setSconeComponent. Stosowanie komponentów wspierających w niektórych przypadkach jest uzasadnione, co nie zmienia faktu, że w pewnych sytuacjach mogą być nadużywane. Nigdy nie powinniśmy mieszać komponentów formularza z danymi biznesowymi w ramach jednego komponentu.
Wyślij dla; D ow olny rodzaj połączenia W ygasa: na końcu sesji Usuń c
U5g$|łs®,j
¡UJsun wszystkie ciasteczka j
I
Zamkną
Dopóki klient nie dezaktywuje znaczników kontekstu, serwer będzie otrzymywał identyfikator sesji wraz z kolejnymi żądaniami tego klienta.
62
Rozdział 2. n Komponenty zarządzane
JavaServer Faces Serwery aplikacji wykorzystują leż strategie alternatywne (np. polegające na przepisywaniu adresów U R L) do obsługi sesji nawiązywanych przez aplikacje klienckie, które nie zwra cają znaczników kontekstu klienta. Strategia przepisywania adresów U R L sprowadza się do dodawania do każdego adresu U R L identyfikatora sesji podobnego do poniższego: http / / corejsf com/ logm/mele* jsp. jses siom d-=P55cd6
d8e
też nie zdaje egzaminu w sytuacji, gdy chcemy zachowywać niezbędne dane pomiędzy żądaniami. W zasięgu żądania powinniśmy umieszczać obiekty tylko wtedy, gdy chcemy je przekazywać do innej fazy przetwarzania w ramach bieżącego żądania. Na przykład znacznik f ■loadBundle umieszcza w zasięgu żądania zmienną pakietu komu nikatów, która jest potrzebna tylko w czasie trwania fazy wizualizacji odpowiedzi dla tego samego żądania.
Aby zweryfikować funkcjonowanie tego m odelu, warto wymusić na przeglądarce B iga odrzucanie znaczników kontekstu klienta z serwera localhost, po czym ponownie uruchomić naszą aplikację internetow ą i wysłać form ularz. Kolejna strona powinna zawierać atrybut js e s s io n id .
I B S Tylko kom ponenty obejm ujące zasięgiem żądania m ają charakter jednowątkowy mJm i jako takie gw arantują bezpieczeństwo wątków. Co ciekawe, jednowątkowe nie są kom ponenty sesyjne. Użytkownik może na przykład jednocześnie wysyłać odpowiedzi z wielu okien przeglądarki internetowej. Każda odpowiedź jes t przetwarzana przez odrębny wątek żądania. Gdybyśmy chcieli zapewnić bezpieczeństwo przetwarzania wielowątkowego w naszych komponentach sesyjnych, powinniśmy opracować odpowiednie mechanizmy blokowania.
Śledzenie sesji za pomocą znaczników kontekstu klienta nie wymaga żadnych dodatkowych działań ze strony programisty aplikacji, a standardowe znaczniki JavaServer Faces automa tycznie zastosują strategię przepisywania adresów U R L w przypadku aplikacji klienckich, które nie obsługują znaczników.
Zasięg sesyjny oznacza, że wszelkie dane są utrwalane od momentu ustanowienia sesji aż do chwili jej zakończenia. Sesja jest kończona wskutek wywołania przez aplikację internetową metody inval ldate obiektu klasy HttpSession lub po upływie przyjętego limitu czasowego. Aplikacje internetowe zwykle umieszczają większość swoich komponentów w zasięgu sesyjnym. Na przykład komponent UserBean może przechowywać informacje o użytkownikach, które będą dostępne przez cały okres trwania sesji. Komponent ShoppmgCartBean może być wypeł niany stopniowo wraz z otrzymywaniem kolejnych żądań składających się na sesję.
63
idnotacje cyklu życia Począwszy od wersji JS F 1.2, istnieje możliwość wskazywania metod komponentów zarzą dzanych, które mają być wywoływane automatycznie bezpośrednio po skonstruowaniu tych komponentów i bezpośrednio przed ich wyjściem poza odpowiedni zasięg. Takie rozwiązanie jest szczególnie wygodne w przypadku komponentów ustanawiających połączenia z zasobami zewnętrznymi, np. bazami danych. Metody komponentu zarządzanego można oznaczać adnotacjami @ P o s tC o n s tru c t lub @ P re D e s lro y : pub1ic c la s s MyBean
Komponenty obejmujące zasięgiem aplikację
\
(SPoSlConsiruCl puDl ic void im u a 11z e t ) {
// kod inicjalizujący
Komponenty obejmujące swoim zasięgiem aplikację są utrzymywane przez cały czas wyko nywania aplikacji. Odpowiedni zasięg jest współdzielony przez wszystkie żądania i wszystkie sesje. Komponenty zarządzane umieszczamy w zasięgu aplikacji, jeśli chcemy, aby były dostępne z poziomu wszystkich egzemplarzy naszej aplikacji internetowej. Taki komponent jest konstru owany w odpowiedzi na pierwsze żądanie dowolnego egzemplarza aplikacji i jest utrzymy wany przy życiu do momentu usunięcia tej aplikacji internetowej z serwera aplikacji.
Komponenty obejmujące zasięgiem żądanie Zasięg na poziomie żądań charakteryzuje się wyjątkowo krótkim czasem życia. Jest konstru owany w momencie wysłania żądania protokołu HTTP i utrzymywany do momentu odesłania odpowiedzi klientowi. Jeśli umieścimy komponent zarządzany w zasięgu żądania, nowy egzemplarz tego kompo nentu będzie tworzony dla każdego żądania. Takie rozwiązanie jest nie tylko kosztowne, ale
} @PreDestroy public void shutdownO { // kod zamykający
} // pozostałe metody komponentu
} Tak oznaczone metody będą wywoływane automatycznie, pod warunkiem że dana aplika cja internetowa zostanie wdrożona w kontenerze zapewniającym obsługę standardu Java Specification Request (JS R ) 250 (patrz http://wwwjcp.org/en/jsr/detail?id=250). Adnotacje w tej formie są obsługiwane przez serwery aplikacji zgodne ze standardem Java E E 5, w tym serwer GlassFish. Oczekuje się, że w niedalekiej przyszłości adnotacje będą obsługiwane także przez kontenery autonomiczne (np. Tomcata).
64
Javaserver Faces
Konfigurowanie komponentów W niniejszym podrozdziale omówimy techniki konfigurowania komponentów w plikach konfiguracyjnych. Ponieważ szczegółowe rozwiązania w tym zakresie mają charakter ściśle techniczny, część Czytelników zapewne zdecyduje się tylko przejrzeć ten podrozdział i wrócić do niego dopiero wtedy, gdy stanie przed koniecznością skonfigurowania komponentów ze złożonymi właściwościami. Najczęściej wykorzystywanym plikiem konfiguracyjnym jest WEB~INF/faces-config.xml. Okazuje się jednak, że możemy ustawienia konfiguracyjne umieścić także w następujących miejscach: ■ W plikach nazwanych META-INF/faces~config.xml w ramach dowolnych plików JA R wczytywanych przez mechanizmy ładowania klas kontekstu zewnętrznego. (Opisywany mechanizm jest wykorzystywany w sytuacji, gdy dostarczamy komponenty wielokrotnego użytku w formie plików JA R ). ■ W plikach zdefiniowanych za pośrednictwem parametru inicjalizacji javax. faces. ^CONFIG_FILES w ramach pliku WEB-INF/web.xml. Przykład definicji tego parametru przedstawiono poniżej: javax.faces.CONFIG_FILES WEB-INF/navigation.xml,WEB-INF/beans.xml
(Opisany mechanizm jest szczególnie korzystny, jeśli korzystamy z narzędzi do projektowania oprogramowania, ponieważ skutecznie izoluje nawigację, komponenty itd.). Dla uproszczenia będziemy w tym rozdziale stosowali tylko plik konfiguracyjny WEB-INF/
^faces-config. xm /. Komponent jest konfigurowany przez element managed-bean wewnątrz elementu faces-config najwyższego poziomu. Dla każdego tak konfigurowanego komponentu musimy określić przynajmniej nazwę, klasę i zasięg: usercom.corejsf.UserBeansession
Zasięg może być reprezentowany przez słowa request, session, appl i cation lub none. Zasięg none oznacza, że dany obiekt nie jest utrzymywany w żadnej z trzech map zasięgów. Obiekty z tym zasięgiem wykorzystuje się w roli elementów składowych podczas wiązania skompli kowanych komponentów. Przykład takiego rozwiązania przeanalizujemy w punkcie „Wiązanie definicji komponentów” w dalszej części tego podrozdziału.
Rozdział 2. ■ Komponenty zarządzane
65
Ustawianie wartości właściwości Nasze rozważania rozpoczniemy od analizy prostego przykładu. Poniżej modyfikujemy egzemplarz komponentu UserBean: usercom.corejsf UserBeansessionnamejapasswordsekret
Kiedy komponent user jest po raz pierwszy odnajdywany, serwer aplikacji tworzy nowy egzemplarz za pomocą konstruktora domyślnego UserBeanO. Następnie są wykonywane metody setName i setPassword. Aby zainicjalizować właściwość wartością nul 1, należy użyć elementu nul 1-val ue. Przykład takiego rozwiązania przedstawiono poniżej: password
Inicjalizacja list i map Do inicjalizacji wartości typu L is t lub Map służy specjalna konstrukcja składniowa. Poniżej przedstawiono przykład inicjalizacji listy: <1ist-entries> java.lang. Integer3l4l5
W powyższym fragmencie kodu użyto opakowania w formie typu danych java.lan g .In teg er, ponieważ struktura Li st nie może zawierać wartości typów prostych. Lista może zawierać wymieszane elementy val ue i nul 1-val ue. Element val ue-cl ass jest opcjonalny — jeśli z niego zrezygnujemy, zostanie wygenerowana lista obiektów klasy ja v a .lang.String.
66
JavaServer Faces
Rozdział 2. ■ Komponenty zarządzane
Z nieco bardziej skomplikowaną sytuacją mamy do czynienia w przypadku map. W odpo wiednich konstrukcjach możemy stosować opcjonalne elementy key class i value class (gdzie typem domyślnym ponownie jest klasa java 1ang. String). Musimy następnie zdefi niować sekwencję elementów map-entry, z których każdy obejmuje element key oraz element value lub nul 1-value. Poniżej przedstawiono przykładową konfigurację struktury typu Map:
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
A 5
*inlns xsi ="htLp //www w3 org/2001/xMLSchema-instance" xsi schemaLocation="http //ja va .sun com/xml/ns/javaee ve rsion= "l 2">
7
8 9
n o -view - id>/again jsp < /navigauon -case> < /navigation-rule>
Diagram przejść pomiędzy stronami przedstawiono na rysunku 3.6.
t u le=Dziec m m e prosty quiz o J a v i e answerButton-Sprawdź odpowiedz Stari0verBuUon=RozpOCZmj od nowa
4 5 6
c or re c t= G ra tu la c je . prawidłowa odpowiedz notCorrect=Przykro nam Twoja Odpowiedz je s t płpdna Spróbuj ponownie! s t i 1IN o tC o rre ct=Przykro nam także tym razem podałeś blednq odpowiedź
l
correctAnswer-Prawidłowa odpowiedz
8
SCOre= Twój
9
tiiankYou=Dzi pk ujemy za udział w qu i z i e
w ynik
p u n k to w y
Z diagramu przedstawionego na rysunku 3.7 wynika, że każdy element navigation rule i navigat ion-case może obejmować dowolną liczbę elementów description, display-name i icon. Wymienione elementy w założeniu mają być stosowane na poziomie narzędzi do wizu alnego projektowania interfejsów, zatem nie będziemy ich poddawać szczegółowej analizie.
Przekierowanie
{0}
{0[
Zaawansowane techniki nawigacji Techniki opisane w poprzednich podrozdziałach powinny w zupełności wystarczyć do im plementacji mechanizmów nawigacji w większości aplikacji. W niniejszym podrozdziale skoncentrujemy się na pozostałych regułach, które można konfigurować za pośrednictwem elementów nawigacji w pliku faces-config.xmI. Diagram składni tych elementów przedsta wiono na rysunku 3.7. Z lektury podrozdziału „Konfigurowanie kom ponentów ” w rozdziale 2 . wiemy, że informacje o nawigacji można umieszczać także w innych plikach konfiguracyjnych niż plik standardowy faces~config.xml.
Jeśli element redirect umieścimy za elementem to view-id, kontener JS P wstrzyma prze twarzanie bieżącego żądania i odeśle klientowi polecenie przekierowania protokołu HTTP. Odpowiedź przekierowania przekazuje przeglądarce adres U RL, pod którym znajduje się kolejna strona. Przeki ero wywanie stron jest mechanizmem wolniejszym niż przekazywanie żądań, ponieważ wymaga dodatkowego cyklu komunikacji z przeglądarką. Z drugiej strony, przekierowanie umożliwia przeglądarce aktualizację zawartości jej pola adresu. Na rysunku 3.8 pokazano, jak zmienia się pole adresu wskutek dodania do pliku konfigura cyjnego następującego elementu przekierowania: suc cess /suce ess j sp c r e d 1rect/>
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.
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).
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
É É | Zarówni progrsimiści technologii JS5F, jak i prctgramiści fr ameworku Struts impl emenK i t u j ą śtr ony inteîm eto w e , korzysta jąc ze zna czników ni estandardowych JSP Okażuje się, że o ile z naczniki fram ewot ku Struts bezpośred nio generują kod H I ‘ML-a, o tyle znaczi lik i JS re p rezen tu ją ko m ponenty niezależn e od technologii zn aczników oraz metchanlzrn wizualizacji odpo wiedzialny za genero« ranie kodu języka HTI'4L. Ta kluczowa róz.nica ułlatwia dostosowyv vanie aplileacji JSF cio potrzeb a ltern aty wnych technologii vvizualiz acji.
Atrybuty wspólne Istnieją trzy rodzaje atrybutów znaczników, które mogą być współdzielone przez wiele znacz ników komponentów HTML-a: ■ atrybuty podstawowe, ■ atrybuty HTM L-a 4.0, ■ atrybuty zdarzeń DHTML-a. Poszczególne typy przeanalizujemy w kolejnych podpunktach.
Atrybuty podstawowe Jak wynika z tabeli 4.4, atrybuty podstawowe są współdzielone przez zdecydowaną większość znaczników HTM L-a technologii JSF . Atrybuty id oraz binding, które można stosować dla wszystkich znaczników HTML-a, odwo łują się do komponentu — atrybut id jest wykorzystywany przede wszystkim przez autorów stron, natomiast atrybut bi ndi ng częściej wykorzystują programiści Javy.
101
Tabela 4.4. Atrybuty podstawowe znaczników języka HTMÛ Atrybut
Typy komponentów
Opis
id
A (25)
Identyfikator komponentu
binding
A (25)
Łączy dany komponent z właściwością komponentu wspomagającego
rendered
A (25)
Atrybut logiczny; wartość fa 1se wstrzymuje wizualizację
styleClass
A (23)
Nazwa klasy CSS (od ang. Cascading Style Sheet)
value
1. 0 , C (19)
Wartość komponentu (zwykle w formie wyrażenia reprezentującego wartość)
valueChangeListener
1(11)
Wyrażenie odwołujące się do metody, która reaguje na zmiany wartości
converter
1 ,0 (1 5 )
Nazwa klasy konwertera
validator
1(11)
Nazwa klasy weryfikującej utworzonej i dołączonej do komponentu
required
1(H)
Atrybut logiczny; wartość true oznacza konieczność wpisania wartości w odpowiednim polu
Komunikat niestandardowy wyświetlany w razie wystąpienia błędu konwersji lub weryfikacji poprawności albo braku wymaganych danych wejściowych
Wszechobecne atrybuty rendered i styleClass mają wpływ na sposób wizualizacji kompo nentów. W dalszej części tego podrozdziału dokonamy krótkiej analizy tych ważnych atrybutów.
Identyfikatory i związki Uniwersalny atrybut id umożliwia realizację następujących zadań: ■ uzyskiwanie dostępu do komponentów JS F z poziomu pozostałych znaczników JSF ; ■ uzyskiwanie referencji do komponentów na poziomie kodu Javy; ■ uzyskiwanie dostępu do elementów HTM L-a z poziomu skryptów.
Atrybuty value i converter umożliwiają definiowanie wartości komponentów oraz ich kon wertowanie z reprezentacji łańcuchowych na obiekty (i odwrotnie).
W tym podpunkcie omówimy pierwsze dwa z wymienionych powyżej zadań. Szczegółową analizę ostatniego zadania można znaleźć w punkcie „Elementy formularza i język JavaScript” w dalszej części tego rozdziału.
Atrybuty validator, required i valueChangeListener można stosować dla komponentów wejściowych; umożliwiają weryfikowanie podawanych wartości oraz reagowanie na ich zmiany. Więcej informacji o mechanizmach weryfikacji poprawności i konwersji można znaleźć w rozdziale 6.
Atrybut i d umożliwia autorom stron odwoływanie się do komponentów z poziomu innych znaczników. Istnieje na przykład możliwość wyświetlenia dla komponentu komunikatu o błę dzie z wykorzystaniem następującej konstrukcji:
A — wszystkie, I — wejściowe, O — wyjściowe, C — polecenia, (n) — liczba znaczników, dla których można stosować dany atrybut.
102
JavaServerFaces
Rozdział 4. ■ Znaczniki standardowe JSF
Atrybut converter (wspólny dla znaczników kontrolek wejściowych i wyjściowych) umoż liw ia nam dołączanie do komponentów mechanizmów konwertujących, tzw. konwerterów. Dla znaczników kontrolek wejściowych możemy dodatkowo stosować atrybut v a lid a to r dołączający do odpowiednich komponentów mechanizmy weryfikujące. Konwertery i me chanizmy weryfikujące zostaną szczegółowo omówione w rozdziale 6.
Identyfikatory komponentów można wykorzystywać także do uzyskiwania odpowiednich referencji na poziomie kodu źródłowego Javy. Za pomocą następującego wyrażenia mogli byśmy uzyskać dostęp do komponentu name na przykład w obiekcie nasłuchującym: UlComponent component = event.getComponentO.findComponent("name").
W przedstawionym wywołaniu metody findComponent mamy do czynienia z pewnym istotnym ograniczeniem: komponent, który wygenerował dane zdarzenie, musi należeć do tego samego formularza (lub tabeli danych) co komponent name. Istnieje lepszy sposób uzyskiwania dostępu do komponentów z poziomu kodu Javy. Komponent można zdefiniować w formie pola egzemplarza jakiejś klasy, zdefiniować dla niego metody zwracające i ustawiające wartość właściwości, po czym użyć w kodzie strony JS F odpowiedniego atrybutu binding:
/>
Atrybutowi binding przypisujemy wyrażenie reprezentujące wartość, które w tym przypadku odwołuje się do właściwości (dostępnej do odczytu i zapisu) komponentu Javy. Więcej infor macji na temat tego atrybutu można znaleźć w podrozdziale „Komponenty wspierające" w rozdziale 2. Implementacja JS F przypisuje tej właściwości komponent interfejsu użytkow nika, dzięki czemu możemy na nim operować z poziomu naszego programu. Istnieje też możliwość programowego tworzenia komponentów, które będą stosowane zamiast komponentów definiowanych w kodzie stron JS F . Na przykład właściwość komponentu formularza slatePrompt można by zaimplementować w następujący sposób: p r i v a t e UlComponent slatePrompt = new u lO u t p u t O . public UlComponent getSt aie Prom pL( ) { return statePrompt. } pu blic void setStatePrompt(UlComponent statePrompt) { }
Kiedy framework JavaServer Faces po raz pierwszy odkrywa wyrażenie reprezentujące wartość w postaci # {form.statePrompt}, wywołuje metodę Form.getStatePrompt(). Jeśli wspomniana metoda zwróci wartość nul 1 (co jest zupełnie naturalne), implementacja JS F utworzy kompo nent zgodnie z opisem zawartym na stronie JSF. Jeśli jednak metoda Form.getStatePrompt() zwróci referencję do komponentu (jak w przypadku powyższego fragmentu kodu), zostanie wykorzystany właśnie ten komponent.
103
Style Możemy stosować — albo w formie konstrukcji wbudowanych (atrybut style), albo w formie klas (atrybut styl eCl ass) — style CSS, które mają wpływ na sposób wizualizowania kompo nentów. W większości przypadków odpowiednim atrybutom przypisuje się stałe łańcuchowe (zamiast wyrażeń reprezentujących wartości):
:
W kolejnym kroku powinniśmy użyć atrybutu styleClass:
'
-
Od tej pory możemy modyfikować wygląd kontrolek h:outputText, aktualizując wyłącznie arkusz stylów styles.css.
Wartości, konwertery i mechanizmy weryfikacji Komponenty wejściowe, wyjściowe, poleceń i tabel danych reprezentują jakieś wartości. Odpowiednie znaczniki biblioteki znaczników języka H TM L, jak h: i nputText czy h:dataTabl e, obejmują atrybut val ue. Za pośrednictwem tego atrybutu możemy definiować wartości w for mie łańcuchów:
W zdecydowanej większości przypadków przypisujemy temu atrybutowi wyrażenia repre zentujące wartości:
Wizualizacja warunkowa Atrybut rendered może być wykorzystywany do włączania komponentów do procesu wizu alizacji strony lub ich wyłączania z tego procesu (w zależności od zdefiniowanych wcześniej warunków). Możemy na przykład uzależnić prezentację przycisku Wyloguj od tego, czy dany użytkownik jest załogowany:
104
JavaServer Faces Aby warunkowo wyświetlać grupę komponentów, należy je umieścić w elemencie h : panel Gr i d obejmującym atrybut rendered. Więcej informacji na len lemat można znaleźć w podrozdziale „Panele" w dalszej części lego rozdziału.
Rozdział 4. ■ Znaczniki standardowe JSF
105
Tabela 4.5. Atrybuty przekazywane standardu HTML 4.Cr
Warto pam iętać o możliwości stosowania operatorów w wyrażeniach reprezentują cych w artości. M ożna by na przykład zdefiniow ać w idok przypom inający panel z zakładkam i przez warunkową (opcjonalną) wizualizację poszczególnych paneli w zależ ności od wybranej zakładki. W takim przypadku należałoby użyć elem entu hrpanelGrid w następujący sposób:
Q
Atrybut
Opis
accesskey (14)
klawisz dostępu (zwykle połączony z systemowymi klawiszami dostępu) umożliwiający aktywowanie danego elementu.
accept ( 1)
Lista oddzielonych przecinkami typów zawaności akceptowanych przez dany formularz.
accepicharset ( 1) Lista oddzielonych przecinkami lub spacjami schematów kodowania znaków dla danego
formularza. Atrybut zbioru znaków HTML-a określa się za pomocą atrybutu JSF nazwanego acceptcharset.
Formularze Działanie aplikacji internetowych, w tym aplikacji JS F , opiera się na formularzach języka H TM L. W tabeli 4.7 wymieniono i krótko opisano wszystkie atrybuty elementu h: form.
W przeciwieństwie do znacznika form języka H TM L znacznik h: form nie obsługuje atrybutów method i action. Ponieważ istnieje możliwość zapisywania stanu klienta (za pośrednictwem mechanizmu pól ukrytych), zrezygnowano z rozwiązania polegającego na przesyłaniu formularzy za pomocą metody GET protokołu HTTP. Ilość danych w takich ukrytych polach może przekraczać rozmiar bufora parametrów żądania typu GET, stąd wszystkie operacje przesyłania formularzy JS F zaimplementowano z wykorzystaniem metody POST. Stosowanie atrybutu anchor nie jest konieczne, ponieważ operacja wysyłania formularza JS F zawsze dotyczy bieżącej strony. (Nawigacja do nowej strony następuje dopiero po wysłaniu danych formularza na serwer). Znacznik h: form generuje element form języka H TM L. Jeśli na przykład w kodzie strony JS F nazwanej /index.jsp użyjemy znacznika h:form bez żadnych atrybutów, mechanizm odpo wiedzialny za wizualizację tej strony wygeneruje następujący kod HTML-a:
Wszystkie kontrolki formularza generowane przez implementację JS F mają nazwy zgodne z następującą konwencją:
Hasło Ją
Pce hasta rozrv ne od pola potwierdzenia hasła'
Potwierdź
Zakończono
Za pomocą atrybutu id możemy przypisywać nazwy generowanym elementom języka H TM L i tym samym umożliwiamy dostęp do tych elementów z poziomu skryptów języka JavaScript:
Kiedy użytkownik klika przycisk, następuje wywołanie funkcji checkPassword JavaScriptu, którą zdefiniowano w następujący sposób: function checkPassword(form) { var password = forni["registerForm:password"].value: var passwordConfirm = form["registerForm:passwordConfirm"].value; i f (password == passwordConfirm) form.submitO: else alert("Pole hasła różni się od pola potwierdzenia hasła!");
ndZwdFormuldrzd.ndZwdKomponentu
gdzie ndzwdFormuidrzd reprezentuje nazwę formularza, do którego należy dana kontrolka, natomiast ndzwaKomponentu reprezentuje nazwę samej kontrolki. Jeśli nie określimy wartości atrybutów id, framework JavaServer Faces utworzy odpowiednie identyfikatory automa tycznie (co było widać na przykładzie przycisku zdefiniowanego w powyższym fragmencie kodu HTML-a). Wówczas uzyskanie dostępu do pola hasła będzie wymagało użycia nastę pującej konstrukcji: document.forms. registerFormL" registerForm:password"J .va 1ue
Strukturę katalogów aplikacji z rysunku 4.1 przedstawiono na rysunku 4.2. Kod odpowiedniej strony JS F przedstawiono na listingu 4.1, natomiast pakiet komunikatów dla polskiej wersji aplikacji przedstawiono na listingu 4.2.
Rysunek 4.2. Struktura katalogów przykładowej aplikacji ilustrującej sposób korzystania ze skryptów języka JavaScript
1. windowTitle=Przykład uzyskiwania dostępu do elementów formularza z poziomu skryptu języka JavaScript 2. namePrornpt=Imię: 3. passwordPrompt=Hasło: 4. confirmPasswordPrompt=Potwierdź hasło:
Jedno- i wielowierszowe pola tekstowe Tekstowe pola wejściowe są podstawą większości współczesnych aplikacji internetowych. Framework JavaServer Faces obsługuje trzy rodzaje takich pól reprezentowane przez na stępujące znaczniki: ■ h:inputText ■ h:inputSecret ■ h:inputTextarea Ponieważ dla wymienionych znaczników można definiować podobne atrybuty, wszystkie te atrybuty wymieniono i krótko opisano w tabeli 4.8. Wszystkie te znaczniki obejmują atrybuty immediate, required, value i valueChangeListener. Atrybut immediate jest wykorzystywany przede wszystkim dla zmian wartości, które mają wpływ na interfejs użytkownika, i jako taki rzadko jest stosowany dla tych znaczników. Atrybut immedi ate znacznie częściej jest wykorzystywany przez takie komponenty wejściowe jak menu czy listy. Więcej informacji na temat tego atrybutu można znaleźć w podrozdziale „Komponenty bezpośrednie” w rozdziale 7. Trzy spośród atrybutów wymienionych w tabeli 4.8 mogą być stosowane tylko dla pojedyn czych znaczników: cols, rows i redisplay. Atrybuty rows i cols można stosować w ramach znacznika h:inputTextarea celem określania odpowiednio liczby wierszy i kolumn wielowierszowego pola tekstowego. Atrybut redisplay (stosowany dla znacznika h: i nputSecret) określa, czy dane pole hasła ma zachowywać swoją wartość i tym samym wyświetlać ją ponownie po odesłaniu formularza. Proste zastosowania znaczników h: i nputText i h: i nputSecret przedstawiono w tabeli 4.9. W wyniku pierwszego przykładu przedstawionego w tabeli 4.9 zostanie wygenerowany następujący kod HTML-a:
Ponieważ opisywane pole wejściowe jest dostępne tylko do odczytu, komponent naszego formularza definiuje tylko metodę zwracającą wartość odpowiedniej właściwości: private String testString = "12345678901234567890". public String getTestString() { return testString;
112
Rozdział 4. ■ Znaczniki standardowe JSF
JavaServer Faces
Tabela 4.8. Atrybuty znaczników h:inputText, h:inputSecretf h:inputTextarea oraz h:inputHidden
Tabela 4.9. Przykłady zastosowań znaczników h:inputText i h:inputSecret
Atrybuty
Opis
Przykład
Elekt
cols
Tylko dla znacznika h: inputTextarea — reprezentuje liczbę kolumn.
¡12345678901234567890
immediate
Infonnuje, że wartość komponentu musi być skonwertowana i zweryfikowana natychmiast, tzn. w fazie stosowania wartości żądania (Apply Request Value).
redisplay
Tylko dla znacznika h: i nputSecret — jeśli ma wartość true, wartość pola wejściowego jest ponownie wyświetlana po ponownym załadowaniu danej strony.
requi red
Wymaga wypełnienia danego komponentu wejściowego w chwili wysyłania formularza.
rows
Tylko dla znacznika h: i nputTextarea — reprezentuje liczbę wierszy.
valueChangeListener
Wskazuje obiekt nasłuchujący, który ma być informowany o zmianach wartości.
label (JSF 1.2)
Opis danego komponentu na potrzeby ewentualnych komunikatów o błędach. Atrybut 1abel nie może być stosowany dla kontrolki h: i nputHi dden.
Atrybuty podstawowe7. Atrybut styl eCl ass nie może być stosowany dla kontrolki
accesskey, alt, dir, disabled, lang, maxlength, readonly, size, style, tabindex, t it le
Atrybuty przekazywane języka HTML 4.08 — atrybuty alt, maxi ength i si ze nie mogą być stosowane dla kontrolki h: i nputTextarea. Żaden z tych atrybutów nie może być stosowany dla kontrolki h: i nputHi dden.
autocomplete
Jeśli przypiszemy temu atrybutowi wartość "off", zostanie przeprowadzony proces wizualizacji niestandardowego atrybutu HTML-a autocomplete="off" (można tę konstrukcję stosować tylko dla znaczników h: i nputText i h: i nputSecret).
h• i nputHidden.
Zdarzenia języka DHTML. Żaden z tych atrybutów nie może być stosowany onblur, onchange, onclick, ondblclick, dla kontrolki h•i nputHi dden9. onfocus, orikeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onselect
(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
ićład
Rysunek 4.3. Plik
Edycja.
i ^ / i l +'
'”'”~t^T®TT^T Widok ^
Histeria ¡0 ,
Zakłaalci |_
Imię
; . ;; v
htTp7 1o c a Ih o st: 8 Q8O/perso f T j ¿¡¿f
[A j
•Lud Lę s p ę a z a c - c z a s : c z y t a j ą c d o b r e k s ią z i-n p r o g r a m u ją c - m t e r t e j s y u z y t t o u m k a w te c h n o lo g u rSF ' l a i n ą i s i ę z m oim p sem
m
Dziękujemy za wpisanie niezbędnych tnformagj -Mo|pa
Plik
^4567S'g01731 t
Pomoc
^ojcech
i W y ś lij S'/v0 ) 8 d a n e o s o b o w e |
Nareędsia
Proszę wpisać następujące dane osobowe
Proszę napisać cos o sobie
cois="5"/>
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:
¡ p ie r w s z y
L u b ię s p ę d z a ć c z a s
(d ru g i (trz e c i
c z y t a j ą c d o b r e K s ią z K i p ro g ia m u ją c m t e r t e j s y u ż y tk o w n ik a w te c h n o l o g i i b a w ią c s i ę z m oim p se m
1 public String getDa ta InRows() { return data[nRows.
Pola ukryte Technologia JavaServer Faces oferuje obsługę pól ukrytych reprezentowanych przez znaczniki h: i nputHi dden. Pola ukryte często są wykorzystywane łącznie z akcjami języka JavaScript do odsyłania danych na serwer. Znacznik h: i nputHi dden obsługuje co prawda te same atrybuty co pozostałe znaczniki kontrolek wejściowych, ale nie obsługuje standardowych znaczników języków H T M L i D H TM L.
Stosowanie jedno- i wielowierszowych pól tekstowych W niniejszym punkcie dokonamy analizy kompletnego przykładu aplikacji wykorzystującej jedno- i wielo wierszowe pola tekstowe. Nasza aplikacja (patrz rysunek 4.3) wykorzystuje kontrolki wejściowe h:inputText, h: i nputSecret oraz h: inputTextarea do gromadzenia niezbędnych informacji o użytkowniku. Wartości tych komponentów interfejsu użytkownika związano z właściwościami komponentu JavaBean, które z kolei są przedmiotem odwołań z poziomu strony Dziękujemy odpowiedzialnej za ponowne wyświetlanie wpisanych danych.
Warto zwrócić uwagę na trzy aspekty prezentowanej aplikacji. Po pierwsze, strony JavaServer Faces odwołują się do komponentu użytkownika (com. corejsf .UserBean). Po drugie, znacznik h : i nputTextarea przesyła tekst wpisany w wielowierszowym polu tekstowym do modelu (w tym przypadku komponentu UserBean) w formie pojedynczego łańcucha obejmującego znaki nowego wiersza (\n). Wspomniany łańcuch jest następnie wyświetlany z wykorzystaniem elementu
języka H T M L celem zachowania oryginalnego formatowania. Po trzecie, w celach prezentacyjnych do formatowania danych wynikowych użyliśmy atrybutu styl e. W rzeczywistej aplikacji najprawdopodobniej użylibyśmy arkuszy stylów, które ułatwiłyby zarządzanie globalnymi zmianami wyglądu strony naszego oprogramowania. Na rysunku 4.4 przedstawiono strukturę katalogów aplikacji przedstawionej na rysunku 4.3. Na listingach od 4.3 do 4.7 przedstawiono kod odpowiednich stron JSF , komponentów zarzą dzanych, pliku konfiguracyjnego i pliku komunikatów.
Tabela 4.11. Atrybuty znaczników h:outputText oraz h:outputFormat Atrybuty
Opis
escape
Jeśli przypiszemy temu atrybutowi wartość true, znaki <, > i &zostaną zastąpione odpowiednimi sekwencjami specjalnymi. Atrybut escape domyślnie ma wartość fal se.
binding, converter, id, rendered, styleClass, value
Atrybuty podstawowe10.
style, title ,d ir (JSF 1.2), lang (JSF 1.2) HTML 4.011.
indexWindowTitle=Przykład użycia jedno- i wielowierszowych pól tekstowych thankYouWindowTitle=Dziękujemy za wpisanie niezbędnych informacji thankYouPageTitle=Dziękujemy! indexPageTitle=Proszę wpisać następujące dane osobowe namePrompt=Imię: passwordPrompt=Haslo: tel!UsPrompt=Proszę napisać coś o sobieaboutYourselfPrompt=Informacje o użytkowniku: submitPrompt=Wyślij swoje dane osobowe
Wyświetlanie tekstu i obrazów Aplikacje JS F wykorzystują następujące znaczniki do wyświetlania danych tekstowych i obrazów: ■ h:outputText ■ h:outputFormat ■ h:graphiclmage h: outputText jest jednym z najprostszych znaczników technologii JavaServer Faces. W ramach wspomnianego znacznika można stosować zaledwie kilka atrybutów, a jego umieszczanie na stronach JS F zwykle nie powoduje generowania odpowiedniego elementu HTML-a. Zamiast znacznika języka H T M L znacznik h: outputText z reguły generuje zwykły tekst — element span HTML-a jest generowany tylko wtedy, gdy w znaczniku h: outputText użyjemy atrybutu style lub styleClass. Warto też pamiętać, że znaczniki h:outputText i h:outputFormat obsłu gują jeden atrybut unikatowy w skali wszystkich znaczników HTML-a technologii JSF: escape. Atrybut escape domyślnie ma wartość false; jeśli jednak przypiszemy mu wartość true, znaki <, > oraz & będą konwertowane odpowiednio na sekwencje &11;, >: i &. Zamia na wymienionych znaków utrudnia ataki przeprowadzane z wykorzystaniem skryptów typu X S S (od ang. Cross-Site Scripting). Więcej informacji na temat tego rodzaju ataków można znaleźć na stronie internetowej http://www.cert.org/advisories/CA-2000-02.html. Atrybuty znacznika h: outputText wymieniono i krótko opisano w tabeli 4.11.
Znacznik h:outputFormat formatuje komunikaty złożone z uwzględnieniem parametrów zdefi niowanych w ciele tego znacznika — poniżej przedstawiono prosty przykład takiej konstrukcji:
W powyższym fragmencie kodu komunikat złożony ma postać {0} ma {1} 1at, a dwa nie zbędne parametry (zdefiniowane za pośrednictwem znaczników f:parani) to Bogdan i 38. Oznacza to, że przedstawiony fragment spowoduje wyświetlenie komunikatu Bogdan ma 38 lat. Znacznik h :outputFormat wykorzystuje do formatowania tego komunikatu egzemplarz klasy ja va .text.MessageFormat. Atrybuty znacznika h: outputFormat wymieniono w tabeli 4.11. Znacznik h:graphic Image umożliwia stosowanie ścieżek do obrazów definiowanych względem kontekstu (ścieżek wyrażonych względem katalogu głównego danej aplikacji internetowej). Element h: graphic Image generuje znacznik i mgjęzyka H TM L. W tabeli 4.12 przedstawiono wszystkie atrybuty znacznika h: graphi clmage.
JavaServer Faces W tabeli 4.13 przedstawiono kilka praktycznych przykładów zastosowań znaczników h: output '-►Text i h:graphiclmage.
Tabela 4.13. Przykłady praktycznych zastosowań znaczników h:outputText i h:graphiclmage Przykład
Efekt
12345678901234567890
Liczba 1000
jwitaj
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.
so lid
jp g"
b l a c k "/>
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 i jeśli atrybutowi escape przypiszemy wartość false (jak w trzecim przy kładzie z tabeli 4.13), znacznik h:outputText w tej formie wygeneruje element input języka HTM L. Okazuje się, że właśnie niezamierzone generowanie elementów HTML-a jest typowym przeoczeniem, którego skutkiem jest możliwość przeprowadzania ataków XSS. Jeśli atrybutowi escape przypiszemy wartość true (jak w czwartym przykładzie z tabeli 4.13), dane wynikowe zostaną przekonwertowane na zupełnie nieszkodliwy tekst, który trudno byłoby wykorzystać potencjalnym hakerom. Dwa ostatnie przykłady z tabeli 4.13 ilustrują możliwe sposoby stosowania znacznika h: graph i c l mage.
Przyciski i łącza Przyciski i łącza są nieodłącznym elementem aplikacji internetowych. Technologia JavaServer Faces oferuje następujące znaczniki obsługujące tego rodzaju konstrukcje: ■
h;commandButton
■
h:commanclLink
■
h :outputL i nk
Trzeci z przykładów przedstawionych w tabeli 4.15 generuje przycisk typu włączony-wylączony (ang. push button), czyli element input typu but ton języka H TM L, którego kliknięcie nie powoduje wysłania formularza. Jedynym sposobem przypisania do tego rodzaju przycisku określonego zachowania jest zdefiniowanie odpowiedniego skryptu dla jednego z atrybutów zdarzeń DHTML-a (podobnie jak w przykładzie ilustrującym znaczenie atrybutu oncl i ck). ■ K 9 Zarówno znacznik h: graphiclm age, jak i znacznik h:command8utton może wyświetlać L i J I obraz; m usim y jed n ak pam iętać, że w technologii JSF 1 ,1 sposób wskazywania obrazów w obu znacznikach nie był identyczny. Znacznik h:commandButton wym agał podania ścieżki do kontekstu, natom iast w przypadku znacznika h;g rap h iclm ag e taka ścieżka była dodaw ana autom atycznie. Poniżej przedstaw iono przykład w skazyw ania tego sam ego obrazu w obu znacznikach w ram ach aplikacji myApp:
W technologii JSF 1 .2 żaden z opisywanych znaczników nie wymaga definiowania pełnej ścieżki do kontekstu. Jeśli przypiszemy odpowiednim atrybutom ścieżki bezwzględne (rozpoczynające się od znaku /), ścieżka do kontekstu zostanie dodana automatycznie. Jeśli podamy ścieżkę względną do innej strony (bez poprzedzającego znaku /), zdefinio wany łańcuch bądź wyrażenie zostanie użyte w niezm ienionej postaci. Znacznik h: corrimandLmk generuje element anchor języka HTM L, który pełni podobną funkcję jak przycisk wysyłania formularza. Praktyczne przykłady zastosowań tego znacznika przed stawiono w tabeli 4.16.
122
Rozdział 4. ■ Znaczniki standardowe JSF
JavaServer Faces
Tabela 4.14. Atrybuty znaczników hxommandButton i hxommandLink
Tabela 4.14. Atrybuty znaczników hxommandButton i hxommandLink — ciąg dalszy
Atrybut
Opis
Atrybut
Opis
action
Jeśli przypiszemy temu atrybutowi łańcuch, jego wartość będzie bezpośrednio wskazywała na zasób wykorzystywany przez mechanizm obsługujący nawigację podczas ładowania kolejnej strony JSF (wskutek aktywacji danego przycisku lub łącza).
Jeśli przypiszemy temu atrybutowi wyrażenie odwołujące się do metody (której sygnatura musi być zgodna ze wzorcem Stri ng nazwaMetody()), zasób docelowy będzie reprezentowany przez łańcuch zwrócony przez tę metodę. actionListener
Wyrażenie odwołujące się do metody o sygnaturze void
Atrybut obsługiwany tylko przez znacznik h:commandLi nk — reprezentuje schemat kodowania znaków właściwy dla wskazywanego zasobu.
[vvyś£]
Atrybut obsługiwany tylko przez znacznik h:commandButton — ścieżka do obrazu wyświetlanego wewnątrz przycisku. Jeśli zdefiniujemy ten atrybut, typem wygenerowanej kontrolki wejściowej HTML-a będzie
image
f kliknij ten przycisk aby wykonać skrypt języka JavaScript ]
image. immediate
type
Atrybut reprezentujący wartość logiczną. Wartość fal se (domyślna) oznacza, że akcje i metody nasłuchujące akcji będą wywoływane na końcu cyklu życia żądania; wartość true oznacza, że akcje i metody nasłuchujące akcji będą wy woływane na początku tego cyklu życia. Więcej informacji na temat atrybutów bezpośrednich można znaleźć w rozdziale 6. W ramach znacznika h: commandButton reprezentuje typ generowanego elementu wejściowego (button, submit lub reset). Typem domyślnym (jeśli nie zdefiniujemy atrybutu image) jest submit. W ramach znacznika h: commandLi nk reprezentuje typ wskazywanego zasobu, czyli np. text/html, image/gif lub audio/basie.
val ue
Etykieta wyświetlana wewnątrz przycisku lub w miejsce łącza. Atrybutowi val ue można przypisywać zarówno łańcuchy, jak i wyrażenia reprezentujące wartości.
binding, id, rendered, styleClass
Atrybuty podstawowe15.
accesskey, alt, coords (tylko znacznik HTML 4.016. h:commandLink), dir (JSF 1.1), disabled (JSF 1.1), href lang (tylko znacznik h: commandLi nk), 1ang, readonly (tylko znacznik h : commandButton), rei (tylko znacznik h: commandLi nk), rev (tylko znacznik h:commandLi nk), shape (tylko znacznik h:commandLi nk), style, tabindex, target (tylko znacznik h: commandLi nk), t it le
Tabela 4.16. Przykłady zastosowań znacznika hxommandLink Przykład
Efekt
zarejestruj
za rejestruj
Kiedy użytkownik klika tak zdefiniowane łącze, atrybutowi val ue elementu anchor jest przypi sywany identyfikator klienta h: commandLi nk, po czym następuje wysłanie właściwego formula rza. Fakt wysłania formularza ma oczywiście wpływ na cykl życia aplikacji JSF , a ponieważ atrybut href ma wartość "#", bieżąca strona będzie ponownie ładowana, dopóki akcja zdefi niowana dla tego łącza nie zwróci wartości innej niż nul 1. Szczegółowe omówienie nawigacji pomiędzy stronami aplikacji internetowych JS F można znaleźć w rozdziale 3. W ciele znacznika h: commandLi nk można stosować dowolną liczbę znaczników JS F języka H TM L — każdy taki element będzie odpowiadał znacznikowi HTML-a wchodzącemu w skład danego łącza. Jeśli na przykład użytkownik kliknie tekst lub obraz wygenerowany na podstawie trzeciego przykładu z tabeli 4.16, formularz właściwy dla tego łącza zostanie wysłany na serwer. W przedostatnim przykładzie przedstawionym w tabeli 4.16 odwołujemy się zarówno do akcji, jak i do metody nasłuchującej akcji właściwej dla definiowanych łączy. Szczegółowe omó wienie mechanizmu nasłuchiwania akcji można znaleźć w podrozdziale „Zdarzenia akcji” w rozdziale 7. W ostatnim przykładzie w tabeli 4.16 osadzono znacznik f : param w ciele znacznika h : command ^ Lin k . Kiedy użytkownik klika tak zdefiniowane łącze, jest tworzony parametr żądania obejmujący nazwę i wartość zdefiniowaną we wspomnianym znaczniku f : param. Taki parametr możemy wykorzystać w dowolny sposób. Przykład użycia podobnego parametru przedstawiono w podrozdziale „Przekazywanie na serwer danych z interfejsu użytkownika” w rozdziale 7. Zarówno znacznik h:commandButton, jak i znacznik h: commandLi nk wysyła żądanie, po czym wywołuje cykl życia aplikacji JSF. Chociaż przytoczone znaczniki w wielu sytuacjach okazują się przydatne, w niektórych przypadkach lepszym rozwiązaniem jest zastosowanie konstrukcji umożliwiających ładowanie zasobów bez wywołań kierowanych do cyklu życia JSF. Powin niśmy wówczas korzystać ze znacznika h :outputLink. Wszystkie atrybuty tego znacznika wymieniono i krótko opisano w tabeli 4.17.
Podobnie jak znacznik h: commandLi nk, tak i znacznik h: outputLi nk generuje element anchor języka H TM L. Z drugiej strony, znacznik h:outputLi nk — inaczej niż znacznik h : commandLi nk — nie generuje kodu języka JavaScript celem upodobnienia zachowań łącza do zachowań przycisku. Wartość atrybutu val ue znacznika h:outputLi nk jest wykorzystywana w roli warto ści atrybutu href kotwicy HTML-a, natomiast ciało tego znacznika jest wykorzystywane do wypełnienia ciała generowanego elementu anchor. Przykładowe zastosowania znacznika h: outputLi nk przedstawiono w tabeli 4.18. Tabela 4.18. Przykłady zastosowań znacznika h:outputLink Przykład
Elekt
Opis
binding, converter, id, lang, rendered, styleClass, value
Atrybuty podstawowe18.
accesskey, charset, coords, dir, disabled (JSF 1.2), hreflang, lang, rel, rev, shape, style,tabindex, target, title , type
HTML 4.019.
oribl ur, oncl i ck, ondbl cl i ck, onfocus, onkeydown, onkeypress, onkeyup, onmousedown, onmousemove, onmouseout, orimouseover, onmouseup
Zdarzenia języka DHTML20.
19 Patrz tabela 4.5. 20 Patrz tabela 4.6.
>
ja v a .n e t
Spis treści
w m
'"!•/// 4/!\,!¿ '
;r^/^^|Prxejdźdokot^
S p is t r e ś c i
W pierwszym przykładzie przedstawionym w tabeli 4.18 zdefiniowano łącze do witryny internetowej http://java.net. W drugim przykładzie wykorzystano w roli źródła adresu U R L i tekstu wartości właściwości składowanych w komponencie JavaBean. Odpowiednie w ła ściwości zdefiniowano w następujący sposób:
}
private String welcomeLinkText = "Przejdź do strony powitalnej": public String getWelconieLinkText() { return welcomeLinkText;
} W trzech ostatnich przykładach w tabeli 4.18 zdefiniowano łącza do nazwanych kotwic w ra mach tej samej strony JS F . Kod języka H T M L opisujący te kotwice przedstawiono poniżej: Wprowadzenie
P § Gdybyśmy pracowali w technologii JSF 1 .1 , m usielibyśm y w ostatnim przykładzie I k J i z tabeli 4 .1 8 użyć znacznika f : verbatim: ■ '
style= "bolder
127
Opx"/>
Spis treści
Oba łącza wskazują na obraz, parametr żądania oraz metodę akcji. Definicje odpowiednich metod przedstawiono poniżej:
Stosowanie łączy poleceń Skoro omówiliśmy już szczegóły związane ze znacznikami JS F definiującymi przyciski i łącza, możemy przystąpić do analizy kompletnego przykładu. Na rysunku 4.5 przedstawiono aplika cję, o której wspominaliśmy w punkcie „Stosowanie jedno- i wielo wierszowych pól teksto wych", z dwoma łączami umożliwiającymi użytkownikowi wybór języka polskiego lub angiel skiego. W odpowiedzi na aktywację łącza odpowiednia akcja zmienia ustawienia lokalne widoku, po czym implementacja JS F ponownie ładuje bieżącą stronę.
Rysunek 4.5.
'
¡||j Przykład użycia łączy poleceń -
Przykład wykorzystania łączy poleceń do zmiany języka
Plik
Edycja
4^ *
Widok
Historia
*Î |f|P
Zakładki
[
rrtp
Narzędzia
:
L
T ir a i
public class ChangeLocaleBean { public String p o lisbAct1on() ( FacesContext context = FacesContext.getCurrentInstancei ). context .getViewRoot() setLoca ledoca le. POL ISh) . return null. I public String englishAct 1onC) ] FacesContext context - FacesContext getCurrenc[nstance(). context getViewRoott) setLocale(Locale.ENGL[Sh). retunn n u l l ,
Pomoc
/'ocalr.0rfiOfdl/fig'n-d" tarp
Proszę wpisać następujące done osobowe Imię:
Obie akcje ustawiają język widoku. Ponieważ nie zdefiniowaliśmy dla naszej aplikacji żad nych reguł nawigacji, po wysłaniu formularza implementacja JS F ponownie załaduje bieżącą stronę. Wyświetlona w ten sposób strona będzie zawierała komunikaty w języku polskim lub angielskim (w zależności od dokonanego chwilę wcześniej wyboru).
Hasło: Proszę napisać coś o sobie: ' I Using Command links - Mozilla Fwefex
Please enter thefollowing personal information Name: Password:
Na rysunku 4.6 przedstawiono strukturę katalogów tej aplikacji. Na listingach od 4.8 do 4.10 przedstawiono kod odpowiednich stron JS F i klas Javy. Listing 4 .11 zawiera plik konfigura cyjny opracowany z myślą o tej aplikacji.
Rysunek 4.6.
fla g s w atr
Struktura katalogów naszej przykładowej aplikacji
C3 MET A- INF 1 D MANIFEST MF ö
WEB-INF
f Ö ! | |
Please tell us about yourself ;
t
I
\ ! Submit your information j
j
|
Odpowiednie łącza zdefiniowano w następujący sposób:
Q ■ Q
c las se s Ö com $ [ 3 c o re js f j- Q C h a n g e L o c a l e B e a n c la s s
i '
Q
U s e r B e a n class
Q
m e s s a g e s p ro p e rtie s
’
Q m e s s a g e s .d e p ro p e rtie s
fa c e s-c o n ftg xm l w eb xm l
Q
styles css
Q
b r ita in _ fla g g if
0
p o f i s h j l a g g if
Q Q
in d e x htm t in d e x jsp
D
th a n k Y o u .js p _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
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
<1abel>
h:selectOneRadio
<1abel> Ser
h:selectManyListbox
Znaczniki selekcji
hiselectManyMenu
■ h:selectManyCheckbox
■ h:selectBooleanCheckbox
■ h:selectOneListbox
''6 ‘ Średnie '"*) Licencjat O "Wyższe O Doktorat
h:selectOneMeriu
Technologia JavaServer Faces oferuje siedem znaczników kontrolek umożliwiających wybór opcji:
B Czerwony BI Niebieski
[sier Marynata Musztarda1 Sa ła ta
132
JavaServer Faces
Rozdział 4. ■ Znaczniki standardowe JSF
Zdecydowanie najprostszym znacznikiem selekcji jest h: sel ectBool eanCheckbox, czyli element reprezentujący pole wyboru, które można łatwo związać z właściwością typu boolean kom ponentu JavaBean. Zbiór pól wyboru można też prezentować na stronie za pomocą znacznika h: sel ec1:ManyCheckbox. Znaczniki, których nazwy rozpoczynają się od przedrostka selectOne, umożliwiają użytkow nikowi zaznaczanie po jednym elemencie kolekcji. Za pomocą tego rodzaju znaczników generujemy zbiory przycisków opcji oraz menu i listy jednokrotnego wyboru. Znaczniki sel ectMany opisują zbiory pól wyboru oraz menu i listy wielokrotnego wyboru. Wszystkie znaczniki selekcji obsługują niemal identyczne zbiory atrybutów (patrz tabela 4.20).
Pola wyboru i przyciski opcji Pola wyboru mogą być reprezentowane przez dwa znaczniki JS F : ■ h:selectBooleanCheckbox ■ h:se lectManyCheckbox Znacznik h: sel ectBool eanCheckbox reprezentuje pojedyncze pole wyboru, które można związać z właściwością typu boolean komponentu JavaBean. Poniżej przedstawiono prosty przykład takiego rozwiązania: Proszę o kontakt g)
Klasa CSS dla kontrolek wyłączonych (atrybut nie może być stosowany dla znacznika h: sel ectBool eanCheckbox.
enabledClass Layout
Klasa CSS dla kontrolek włączonych (atrybut nie może być stosowany dla znacznika h: sel ectBool eanCheckbox. Określa sposób rozmieszczenia elementów: 1i neDi recti on (poziomo) lub pageDi recti on (pionowo). Atrybut może być stosowany tylko dla znaczników h: sel ectOneRadi o i h: sel ectManyCheckbox.
label (JSF 1.2)
Opisuje komponent, który należy wykorzystać w razie konieczności wyświetlenia komunikatu o błędzie.
binding, converter, converterMessage
Atrybuty podstawowe21.
(JSF 1.2), requiredMessage (JSF 1.2), id, immediate, styleClass, required, rendered, validator, validatorMessage (JSF 1.2), value, valueChangeListener accesskey, border, dir, disabled, lang, HTML 4.022 — atrybut border można stosować tylko dla znaczników readonly, style, size, tabindex, t it le h: sel ectOneRadi o i h; sel ectManyCheckbox. atrybut size można stosować dla znaczników h: sel ectOneLi stbox i h;selectManyListbox. onblur, onchange, onclick, ondblclick, Zdarzenia języka DHTML23. onfocus, onkeydown, onkeypress, orikeyup, onmousedown, onmousemove, onmouseout, onmouseover, onmouseup, onselect
133
W kodzie strony JS F należy umieścić następującą konstrukcję:
W komponencie wspierającym powinniśmy zdefiniować następującą właściwość dostępną do odczytu i zapisu: private boolean contactMe, public void setContactMe(boolean newValue) { contactMe = newValue;
} public boolean getContactMeO { return contactMe;
} Na tej podstawie zostanie wygenerowany następujący znacznik języka H TM L:
Grupę pól wyboru można utworzyć za pomocą znacznika h: sel ectManyCheckbox. Już na podstawie jego nazwy można się domyślić, że istnieje możliwość zaznaczenia jednego lub wielu pól wyboru wchodzących w skład takiej grupy. Pola wyboru tej grupy definiujemy w ciele znacznika h: sel ectManyCheckbox (w formie jednego lub wielu znaczników f: select Item lub jednego znacznika f : select Items). Więcej informacji o tych znacznikach można znaleźć w punkcie „Elementy” w dalszej części tego podrozdziału. Poniżej zdefiniowano przykłado wą grupę pól wyboru umożliwiających wybór kolorów: fU Czerwony @ Niebieski □ Zoty p Zielony 0 Pomarańczowy Znacznik h: sel ectManyCheckbox mógłby mieć następującą postać:
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:
Każdy kolor jest reprezentowany przez odrębny element i nput otoczony znacznikiem 1abel i ułatwiający uzyskiwanie dostępu do jego stanu. Element 1abel jest z kolei umieszczany w ciele znacznika td. Przyciski opcji zaimplementowano w formie znacznika h:selectOneRadio. Poniżej przed stawiono efekt jego użycia w kodzie strony JS F : O Średnie O Licencjat Ęj Wyższe O Doktorat Atrybut va 1ue znacznika h: sel ectOneRad i o reprezentuje aktualnie zaznaczony element. Także w tym przypadku wypełnienie przycisków opcji wymaga użycia wielu znaczników f •sel ect I tern:
Podobnie jak znacznik h:selectManyCheckbox, tak i znacznik h: seiectOneRadio generuje tabelę języka H TM L. Poniżej przedstawiono kod tabeli wygenerowanej na podstawie powyższego fragmentu:
<1abel for="Jd2:_idl4"> Średnie
Generowanie tabel HTM L-a nie jest jedyną cechą wspólną znaczników h: sel ectOneRadi o i h: sel ectManyCheckbox — w obu możemy też stosować unikatowe atrybuty: ■ border ■ enabledClass
135
■ disabledClass ■ 1ayout Atrybut border określa szerokość obramowania. Poniżej przedstawiono przykładowe przy ciski opcji i pola wyboru z obramowaniami o grubości odpowiednio jednego i dwóch pikseli: ¡ O Ś r e d n ie j# L ic e n c ja t ] £> W y fc s a e jO D o k to r a tj f f i C z e rw o n y j S I N ie b ie s k i ¡121 Z ś łty ¡ 0 -Z ie lo ny j Q P om arań czow y j
Atrybutowi 1ayout możemy przypisać wartość 1meDirection (w poziomie) lub pageDi rect i on (w pionie). Dla grupy pól wyboru po lewej stronie zastosowano układ pageDi rect i on, natomiast dla grupy pól wyboru po prawej stronie zastosowano układ 11neDi rect i on: Ci Czerwony 1 Niebieski
Część programistów nie rozumie, dlaczego atrybutowi layout nie można przypisywać wartości horizontal ani vertical (zam iast odpowiednio lineDirection i page ^D irection). O ile w językach wywodzących się z łaciny kojarzenie określeń lin e ^Direction i pageDi rect i on z orientacją poziomą i pionową jes t dość naturalne, w innych językach podobne związki nie są tak oczywiste. Na przykład przeglądarki wyświetlające strony w języku chińskim, gdzie tekst je s t prezentowany z góry do dołu, mogą interpre tować w artość lineDirection jako układ pionowy i wartość pageDi rect i on jak o układ poziomy.
Menu i listy Menu i listy są reprezentowane przez następujące znaczniki JS F języka H TM L: ■ h:selectOneListbox ■ h:selectManyLi stbox ■ h:selectOneMeriu ■ h:selectManyMenu Atrybuty, które można stosować w tych znacznikach, wymieniono już w tabeli 4.20, zatem nie będziemy ich poddawać ponownej analizie.
136
JavaServer Faces Znaczniki menu i list generują elementy select języka H TM L. Znaczniki menu dodają do generowanego elementu select atrybut size="l". Właśnie wspomniany atrybut jest jedynym aspektem różniącym konstrukcje reprezentujące menu od konstrukcji reprezentujących listy.
Rozdział 4. ■ Znaczniki standardowo JSF
137
Do definiowania menu służą znaczniki h : selectOneMenu i h:selectManyMenu. Poniżej przed stawiono przykład menu jednokrotnego wyboru:
__
Przykład listy jednokrotnego wyboru przedstawiono poniżej: Znacznik h: sel ectOneMenu reprezentujący menu w tej formie ma następującą postać: 1902 1903 1904
Odpowiedni znacznik listy będzie miał następującą postać:
Łatwo zauważyć, że liczbę widocznych elementów listy określiliśmy za pośrednictwem atry butu si ze. Na podstawie tego kodu zostanie wygenerowany następujący kod języka H TM L:
Stosując znacznik h:selectManyListbox, możemy definiować listy wielokrotnego wyboru podobne do tej, którą przedstawiono poniżej:
A tak wygląda odpowiedni kod języka H TM L:
Za pomocą znacznika h: sel ectManyMenu możemy definiować menu wielokrotnego wyboru. Na jego podstawie jest generowany kod HTML-a podobny do fragmentu przedstawionego poniżej:
Okazuje się, że przedstawiony kod języka H TM L powoduje różne rezultaty w różnych przeglą darkach internetowych. Poniżej przedstawiono odpowiednie kontrolki wyświetlone w oknach przeglądarek Internet Explorer (z lewej strony) i Mozilla Firefox (z prawej strony): Znacznik opisujący tę listę ma następującą postać:
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:
H H W języku HTML rozróżnienie pomiędzy menu a listami je s t dość sztuczne. Zarówno H ü l m enu, ja k i listy są reprezentowane przez elem enty select tego języka. Jedynym a sp e ktem dzielącym obie konstrukcje je s t stały atrybut $i ze=’T Tstosowany dla wszyst kich m enu. Zgodnie z naszymi oczekiwaniami wszystkie przeglądarki wyświetlają menu jednokrotnego wyboru w form ie list rozwijanych. Okazuje się jed n ak , że podobna spójność nie je s t zachow ana w przypadku m enu wielokrotnego wyboru definiowanych z wykorzystaniem atrybutów size—ł " i multiple. Za m ia st list rozwijanych umożliwiających wybór wielu elem entów — co byłoby zupełnie naturalne — niektóre przeglądarki wyświetlają dziwacz ne konstrukcje, ja k niem al bezużyteczne, m ikroskopijne paski przew ijania (In tern et Explorer 6 ), lub w cale nie w yśw ietlają pasków przew ijania, co w ym aga korzystania z klawiszy strzałek (Firefox 1 .5 ).
138
Rozdział 4. ■ Znaczniki standardowe JSF
JavaServer Faces Od początku podrozdziału „Znaczniki selekcji” konsekwentnie wykorzystywaliśmy wiele znaczników f:selectltem do wypełniania komponentów select. Skoro opanowaliśmy już podstawy znaczników selekcji, warto przystąpić do bliższej analizy znacznika f :selectltem i znacznika pokrewnego f : sel ectltenis.
Elementy Wszystkie znaczniki selekcji poza elementem h: sel ectBool eanCheckbox wykorzystują do definiowania dostępnych elementów znaczniki f : sel ectltem lub f : sel ectItems. Te kluczowe znaczniki przeanalizujemy w kolejnych podpunktach tego punktu (począwszy od znacznika f : selectltem ).
Znacznik f:selectltem Za pomocą znacznika f : sel ectltem możemy definiować elementy jednokrotnego wyboru:
Wartości w poszczególnych elementach (Ser, Marynata itd.) są przekazywane w formie wartości parametrów żądania w czasie wysyłania formularza, do którego należy tak zdefiniowane menu. Wartości przypisane atrybutom i temLabel są wykorzystywane w roli etykiet elementów menu. W pewnych sytuacjach warto rozważyć stosowanie innych wartości parametrów żądania i innych etykiet elementów: <1 selectltem itemValue="l" itemLabel-"Ser"/>
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.
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:
Wyrażenie reprezentujące wartość #{ form, condiment Items} może wskazywać np. na tablicę obiektów klasy Sel ect I tern: private static Selectltem[] condimentltems = { new Selectltemd, "Ser"), new Selectltem(2. "Marynata"), new SelectltemO, "Musztarda"), new Selectltem(4, "Sałata"), new Selectltem(5, "Cebula")
}.
public Selectltem[] getCondimentItems() { return condimentltems:
Atrybutowi value znacznika f: sel ect Items musimy przypisać wyrażenie reprezentujące wartość, które wskazuje na jedną z następujących konstrukcji: ■ pojedynczy obiekt klasy Sel ect Item; ■ kolekcję obiektów klasy Sel ect Item; ■ tablicę obiektów klasy Sel ectltem; ■ mapę, której elementy reprezentują etykiety i wartości obiektów klasy Sel ectltem. | Programistom, którzy nie potrafią zapam iętać możliwych konstrukcji przypisywanych I atrybutowi value znacznika f: sel ect Items, być może pomoże następujący skrót: PKTM , czyli Pojedynczy elem ent selekcji, Kolekcja elem entów selekcji, Tablica elementów selekcji oraz M a p a .
P S I Stosow anie pojedynczego znacznika f :selectltem$ zwykle je s t lepszym rozwią■ ■ ¡a zaniem niż korzystanie z wielu znaczników f: sel ectltem. Jeśli użyjemy znacznika f: sel ectltem, zm iana liczby elem entów będzie się wiązała z koniecznością zmodyfiko wania zarówno kodu Javy, ja k i kodu stron JSF. W przypadku znacznika f : sel ect I tenis ta k a zm iana spowoduje, że będziem y m usieli dostosować tylko kod Javy, Jeśli przypiszemy temu atrybutowi mapę, implementacja JS F utworzy po jednym obiekcie klasy Sel ectltem dla każdego elementu (pary) w ramach tej mapy. Klucze tych par zostaną wykorzystane w roli etykiet elementów, natomiast wartości zostaną użyte w roli wartości elementów. Poniżej przedstawiono przykład definicji składników potrawy właśnie w formie struktury mapy:
Rozdział 4. ■ Znaczniki standardowe JSF
141
private Map condimentItem = null: public Map getCondimentItems0 { i f (condi nient Item == null) { condi nient Item = new LinkedHashMap() condimentltem.putC'Ser", 1): / / etykieta, wartość condinientItem.put("Marynata", 2) : condimentltem.put( "Musztarda", 3), condimentltem.putCSałata", 4), condimentItem.put( "Cebula", 5);
i
return condimentltem;
Warto pamiętać, że jeśli zdecydujemy się na wykorzystanie struktury mapy, nie będziemy mogli definiować opisu ani stanu poszczególnych elementów. Stosując obiekt klasy Sel ectltem, z natury rzeczy wiążemy nasz kod z interfejsem JS F A PI. Tylko użycie struktury typu Map eliminuje tego rodzaju związki, co czyni z tego rozwiązania atrakcyjną alternatywę względem pozostałych dostępnych konstrukcji. Przed podjęciem osta tecznej decyzji warto jednak mieć na uwadze następujące aspekty: 1.
Większość programistów preferuje stosowanie struktur typu LinkedHashMap (zamiast struktur TreeMap czy HashMap). Klasa LinkedHashMap daje nam kontrolę nad porządkiem elementów, ponieważ kolejność ich przeszukiwania odpowiada kolejności ich dodawania do tej struktury. Gdybyśmy użyli struktury typu TreeMap, etykiety prezentowane użytkownikowi (reprezentowane przez klucze tej mapy) zostałyby posortowane w kolejności alfabetycznej. Takie zachowanie nie zawsze jest zgodne z naszymi oczekiwaniami. Nie chcielibyśmy na przykład, aby lista dni tygodnia obejmowała elementy w następującej kolejności: czwartek, niedziela, piątek, poniedziałek, sobota, środa, wtorek. Co ciekawe, gdybyśmy użyli struktury typu HashMap, elementy zostałyby uporządkowane w sposób zupełnie przypadkowy.
2. Klucze mapy są przekształcane w etykiety elementów, natomiast wartości są wykorzystywane w roli wartości elementów. Kiedy użytkownik zaznacza jakiś element, komponent wspomagający otrzymuje odpowiednią wartość (zamiast klucza). Na przykład w przypadku kodu przedstawionego powyżej przekazanie do komponentu wspomagającego wartości 5 będzie się wiązało z koniecznością iteracyjnego przeszukania elementów celem odnalezienia pasującego klucza ("Cebul a"). Ponieważ wspomniana wartość jest z perspektywy naszej aplikacji łatwiejsza w interpretacji niż etykieta, opisany mechanizm nie stanowi większego problemu (jednak musimy o nim pamiętać).
Crupy elementów Elementy menu i list można grupować (zobacz rysunek na następnej stronie). Poniżej przedstawiono znaczniki JS F definiujące tę listę:
Edycja komórek tabeli Edycja tabeli wymaga umieszczenia w jej komórkach komponentów wejściowych. Na ry sunku 5.7 przedstawiono stronę aplikacji umożliwiającej edycję zawartości wszystkich ko mórek tabeli. Aby edytować pola tekstowe w danym wierszu, należy kliknąć pole wyboru w kolumnie Edytuj. Po zakończeniu edycji użytkownik powinien kliknąć przycisk Zapisz zmiany. Kolejne etapy edycji widać na następujących po sobie zrzutach z rysunku 5.7.
178 *
JavaServerFaces
Rozdział 5. ■ Tabele danych
Edycja komórek tabeli -Mosilla PI*
goycj»
Widok
H.stona
-m7
Zakładki
a
Edytuj Nazwisko
Narzędzia
Pomoc
<-h inputText va lue=' tt{ name last}' rendered='#{name.editable)' size= 10'/>
<>
;
179
^
Linie
Popierała
W»iciech
K owalska
ty
Edycja komórek tabeli -Mozifo Firefcw Widok
Historia
Zakładki
Narzędzia
Pomoc
0
_http://localhost:8080/editi_[» | '.!►] fO J n iocol-:
I ¿api$?¿iruai»
Edytuj
N azwisko
®
Dopie'8'o
J ■
K owalska
l_!
Less zvnski
Wmuzoi eł
■'Oiaeen
ty
Edycja komórek tabeli -Mozilła Firnie».
Plik
Edycja
Widok
Historia
Zakładki
(¡łanzęiizia
“%
'
Poijtoc
♦ * # -® %i {¡¡| f , http:^to>o'L8n«(yed,t:i. Edytuj
Powyższy fragment kodu obejmuje wyłącznie konstrukcje definiujące kolumny pól wyboru i nazwisk. Wartość pola wyboru zależy od tego, czy odpowiednie imię i nazwisko jest edytowalne; jeśli tak, pole wyboru jest zaznaczone. Kolumna nazwiska zawiera w każdym wierszu po dwa komponenty interfejsu użytkownika: h-inputText i h;outputText. Jeśli nazwisko ma być edytowane, jest wyświetlany komponent wejściowy. Z drugiej strony, jeśli edycja nazwiska ma być niemożliwa, we wspomnianej kolumnie jest wyświetlany komponent wyjściowy.
Nazwisko
i^.
■'■■x -pianst ■
r
Eeszczvnsk
K •■'walska
ty
Edycja komórek tabełi - Mo; .Ita c -efos
Wjnczor4
E dytuj
N azwisko
#
-'-"vsoiafst•
■•
Kowalska
U
Reszczvnski
L
Wjnczo,^
linie :n. Kj
ty
Edycja komórek tabeli -Mozilla firefox';
PWc
fcaycją
Wiacie
Historia
■*0®' 4 i# * @ Edytuj Nazwisko
Zakładki
Narzędzia
Pomoc
(j£ ! http://iocaP.-st8n,ftn/.dit-j Lmie
9.7vspiaiiski EcnrvL Kowalska
Kompletny kod strony JS F z rysunku 5.7 przedstawiono na listingu 5.I l. Pakiet komunikatów dla tej aplikacji przedstawiono na listingu 5.12. Układ katalogów analizowanej aplikacji jest identyczny jak w przypadku aplikacji prezentowanej we wcześniejszej części tego rozdziału (patrz rysunek 5.2).
niina
Rrszczvnski Mariusz
LiStiny 5.11. Kod strony editing/web/index.jsp__________________________________________________________
Wmczorek Jan
1 ; ¿SptŁŁ ¿mi&fiy j
2
<°4@
3 4 5 uzono
Rysunek 5.7. Edycja komórek tabeli Komórki tabeli z rysunku 5.7 zawierają komponenty wejściowe, jeśli odpowiednie komórki są wyświetlane w trybie edycji, oraz komponenty wyjściowe, jeśli użytkownik nie zaznaczył właściwych pól wyboru. Sposób implementacji tego rozwiązania przedstawiono poniżej:
—>
Atrybuty rowCl asses i col umnCl asses znacznika h: dataTabl e wykluczają się wza jem nie. Jeśli zdefiniujemy oba atrybuty jednocześnie, atrybut col umnCl asses będzie m iał wyższy priorytet.
Style stosowane dla kolumn Poniższy kod ilustruje sposób, w jaki zastosowano klasy C SS dla tabeli z rysunku 5.8: <11nk nref=’styles css' rel =' stylesreet1 Lype='text/css'/>
Kod samych klas C SS przedstawiono poniżej:
Style
.orders { border- thin solid black;
W ramach znacznika h: dataTabl e możemy stosować atrybuty wskazujące na klasy C SS dla następujących elementów:
}
■ całej tabeli (atrybut styl eCl ass); ■ nagłówków i stopek kolumny (atrybuty headerCl ass i footerCl ass); ■ pojedynczych kolumn (atrybut col umnCl asses); ■ pojedynczych wierszy (atrybut rowCl asses). Na rysunku 5.8 przedstawiono tabelę skonstruowaną z wykorzystaniem atrybutów styl eCl ass, headerClass i col umnClasses.
.evenColumn { text-align: center, background: PowderBlue;
181
182
Rozdział 5. ■ Tabele danych
JavaS erver Faces Mimo że nasza tabela obejmuje pięć kolumn, zdefiniowaliśmy dla nich tylko dwie klasy CSS. W prezentowanej aplikacji znacznik h:dataTable wielokrotnie wykorzystuje istniejące klasy (począwszy od pierwszej). Definiując klasy tylko dla dwóch pierwszych kolumn, możemy łatwo określić style C SS dla kolumn parzystych i nieparzystych.
Style stosowane dla wierszy
183
Tabele bazy danych Ponieważ bazy danych składują informacje w tabelach, komponent tabel danych JS F wprost idealnie nadaje się do prezentowania danych składowanych w bazie danych. W niniejszym podrozdziale przeanalizujemy sposób wyświetlania wyników zapytania wykonywanego na bazie danych.
Za pośrednictwem atrybutu rowClasses możemy wskazywać klasy C SS dla poszczególnych wierszy (zamiast kolumn). Przykład strony skonstruowanej z wykorzystaniem tego atrybutu przedstawiono na rysunku 5.9. Tabelę danych zaimplementowano w następujący sposób:
Stronę aplikacji JS F prezentującej tabelę bazy danych przedstawiono na rysunku 5.10. Í
Rysunek 5.10. Wyświetlanie tabel bazy danych
Prezentacja tabel bazy W.dok
fi Hutona
Zakładki
Narzędzia
Pomoc
:;;€^
http./7iocalhost;8030/database/inaeA.facaJ j *
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:
Podobnie jak w przypadku klas stylów przypisywanych kolumnom, także klasy właściwe dla wierszy są wykorzystywane wielokrotnie, jeśli ich liczba jest mniejsza niż liczba wier szy. W powyższym fragmencie kodu wykorzystaliśmy tę cechę znacznika mdataTable do zdefiniowania klas C SS dla wierszy parzystych i nieparzystych. ■ fflj Z myślą o większej czytelności stosujemy w naszych klasach stylów CSS angielskie L j i nazwy kolorów, np. PowderBłue i MediumTurąuoise. W prawdziwej aplikacji lepszym rozwiązaniem byłoby stosowanie odpowiednich stałych szesnastkowych, które (w prze ciwieństwie do nazw) gwarantują właściwą przenośność.
Komponent zarządzany customer odpowiada za nawiązywanie połączenia z bazą danych i wykonywanie zapytania zwracającego zbiór wszystkich reprezentowanych tam klientów. Wspomniane zapytanie możemy wykonać za pośrednictwem metody CustomerBean.al 1.
184
JavaServer Faces
Rozdział 5. ■ Tabele danych
W przytoczonym powyżej kodzie strony JS F uzyskujemy dostęp do danych w kolumnach, odwołując się do ich nazw — na przykład wyrażenie #{customer. Cust_ID} odwołuje się do kolumny Cust_ID. Strukturę katalogów przykładowej aplikacji przedstawiono na rysunku 5.11. Na listingach od 5.13 do 5.16 przedstawiono kod strony, komponentów i plików konfiguracyjnych.
Rysunek 5.11. Struktura katalogów przykładowej aplikacji prezentującej informacje z bazy danych
¡ 3 d a ta b a s e .w a r f j
META-INF L D MANIFEST.MF WEB-INF
| y - i ¡ 3 c la s se s ¡ j y C2I com ! | f Q c ore js f | |
j
j~ D L~Q
\
C u s t o m e r B e a n .c la s s m e s s a g e s , p ro p e rtie s
j
| D fa ce s- co n fia .xml
|
'‘- Q
w eb . xml
f •£ 2 mise j
L- Q
| D r D [ D
custom ers, sql
styles, css index.htm l in d e x .jsp ___________________________
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);
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).
Kiedy użytkownik klika przycisk Usuń zaznaczone nazwiska , implementacja JavaServer Faces wywołuje metodę TableData.deleteNames. Metoda deleteNames uzyskuje referencję do bieżącej tablicy nazwisk za pośrednictwem metody getWrappedData modelu danych, po czym tworzy nową tablicę (już bez nazwisk przeznaczonych do usunięcia) i umieszczają w modelu za pomocą metody setWrappedData. Mimo że prezentowany przykład nie ilustruje operacji dodawania wierszy, dobrze demonstruje ogólną procedurę — w pierwszej kolejności należy użyć metody getWrappedData() celem uzyskania bieżącego obiektu opakowanego przez model, następnie należy ten obiekt zmo dyfikować (przez dodanie lub usunięcie wierszy), po czym odświeżyć ten obiekt za pomocą metody setWrappedDala().
Metoda deleteNames uzyskuje referencję do bieżącego zbioru nazwisk za pośrednictwem metody getWrappedData modelu, po czym tworzy nową tablicę, której rozmiar jest równy rozmiarowi tablicy bieżącej pomniejszonemu o liczbę nazwisk przeznaczonych do usunięcia. Ta sama metoda dodaje następnie bieżące nazwiska do nowej tablicy z pominięciem nazwisk przeznaczonych do usunięcia. 1wreszcie metoda deleteNames wywołuje metodę setWrappedData modelu celem jego odświeżenia z nową tablicą nazwisk.
Pomoc
'^caihost:8080rediti i 1
Na rysunku 5.13 przedstawiono strukturę katalogów tej aplikacji. Na listingach od 5.17 do 5.19 przedstawiono zawartość plików składających się na aplikację z rysunku 5.12.
Edytui il|
Edytuj 1
Rysunek 5.13.
¡ 2 delete.war
Struktura katalogów aplikacji demonstrującej usuwanie wierszy
f - t S m e t a -inf \ ’L Q MANIFEST.MF
|
|
|
!
f a
\... Q Name.class
|
|
f~ Q TableData. class
\
|.0 faces-config.xml
|
!..D web. xml
| D styles, css | D index.html L- D index.jsp_________________________
i p i Jednym ze sposobów usuwania wierszy jes t wywoływanie metody setWrappedData () ■ M d m odelu, która odświeża dane modelu, istnieje też możliwość odświeżenia sam ego
Na kolejnych zrzutach przedstawionych na rysunku 5.12 widać kompletną procedurę usu wania pojedynczego wiersza. Kiedy użytkownik klika przycisk Usuń zaznaczone nazwiska , implementacja JS F wywołuje metodę obiektu nasłuchującego akcji, której zadaniem jest usunięcie zaznaczonych wierszy. Wspomnianą metodę zaimplementowano w następujący sposób: public String deleteNames() { i f ( IgetAnyNamesMarkedForDeletionO) return n u li;
Na rysunku 5.15 przedstawiono aplikację omówioną w podrozdziale „Edycja komórek tabeli"" i przebudowaną z myślą o obsłudze kolumn tabeli umożliwiających sortowanie.
} public int gelNumperOfNamesMarkedForDeletionO { Name[] currentNames - ( Namef]) modelgetWrappedData(). int cnt = 0. for (int i =0. i < currentNames length, Name name - (Name) currentNames[i]. if (name isMarkedForDe1et ionO ) +--cnt. } return cnt.
193
Rysunek 5.15.
|||i Przykład komponentu sortuj;
Sortowanie zawartości kolumn tabeli
{
Plik
Edycja
\Vtdok
Histona
Firefox Zakładki
Narzędzia
Pomoc
lisim N azw isk o Wmczor K owalska
Anna
D opier ała
W oj cie ch
Reszczyński M< PI«
Edycja
Widok
Historia
4 lU ff ł | I
f* } | I
Zakładki
Narzędzia
Pomoj
http://localhost.8080/sorti ▼
} U suń N azw isko IH
public boolean getAnyNamesMarkedForDeletionO { Named currentNames = (Named) model .getWrappedDataO; for(int i = 0; i < currentNames.length; ++i) { Name name = (Name) currentNames[i]; i f (name.isMarkeclForDeletionO) return true; } return false; } }
W iem y już, jak wykonywać proste operacje na modelu danych. W pewnych sytuacjach mu simy jednak stosować bardziej wyszukane formy przetwarzania polegające np. na sortowaniu lub filtrowaniu danych.
Dopierała [^W ojciech K ow alska
file;///C;/Users/Amol d/Do
Anna
Reszczyński Mariusz Winczorek Jan
Aplikacja przedstawiona na rysunku 5.15 umożliwia sortowanie kolumn tabeli, ponieważ dekoruje oryginalny model danych tabeli. Aby takie rozwiązanie było możliwe, w pierwszej kolejności musimy odpowiednio zdefiniować atrybut val ue znacznika h: dataTabl e:
Sortowanie i filtrowanie Aby sortować lub filtrować zawartość tabel reprezentowanych przez znacznik h: dataTabl e, musimy zaimplementować model tabeli dekorujący jeden z modeli już istniejących (wymie nionych na początku tego podrozdziału). To, na czym faktycznie polega dekorowanie modelu tabeli, przedstawiono na rysunku 5.14.
Rysunek 5.14.
Metoda Tabl eData. names zwraca model danych: public class TableData { private DataModel filterModel= nuli; private static final Name[] nanieś = { new NameC'Anna", "Kowalska"), new NameCJan", "Winnicki"), new NameCMariusz", "Reszczyński"), new Name( "Wojciech", "Dopierała"),
};
Filtr modelu danych
Obiekty klasy UIData (czyli komponentu związanego ze znacznikiem h; dataTabl e) wywołują metody swojego modelu. Jeśli zdecydujemy się udekorować ten model, nasz model (dekorator) musi przechwytywać te wywołania. Model dekoratora musi kierować te wywołania dalej, do modelu oryginalnego (z wyjątkiem metody setRowIndex zwracającej posortowane indeksy, zamiast oryginalnych indeksów modelu). Funkcjonowanie tego mechanizmu szczegółowo przeanalizujemy w dalszej części tego punktu.
public TableDataO { ArrayDataModel model = new ArrayDataModel(names); filterModel = new SortFiIterModel(model);
} public DataModel getNamesO { return filterModel;
} }
W momencie tworzenia obiektu tabl eData tworzony jest także egzemplarz klasy ArrayDataModel, którego konstruktor otrzymuje na wejściu tablicę nazwisk. Jest to nasz model oryginalny. W kolejnym kroku konstruktor klasy TableData opakowuje ten model modelem sortującym.
194
JavaServer Faces
Rozdział 5. ■ Tabele danych
Od tej chwili w wyniku wywołań metody get Names (odpowiedzialnej za wypełnianie tabeli danych) otrzymujemy model sortujący, który zaimplementowano w następujący sposób: public class SortFilterModel extends DataModel { private DataModel model: private Row[] rows; public SortFilterModel(DataModel model) this.model = model; int rowCnt = model .getRowCountO; i f (rowCnt != -1) { rows = new Row[rowCnt]; for (int i=0, i < rowCnt; ++i) rows[ i ] = new Row(i);
Rysunek 5.16.
S3 sorting.war ę C3 META-INF
Struktura katalogów analizowanej aplikacji
j
;
Q MANIFEST.MF
f £3 WEB-INF j y classes
! ! f Pcorn
{
I
|
|
j
j
L Q Name, class
;
j
i Q SortFilterModel$l.class
j
i
I Q SortFilterModel$2.class
\
j
IQ
\
]
$ (¡3 corejsf
So rtFilte rM odel $ Row. cIas s
j Q SortFilterModel.class
I
I
! Q TableData.class
j
|
! Q messages, properties
i
1 D faces-contlg *ml Q weto m1
i public void setRowIndex(int rowIndex) { if (rowlndex == 1 || rowlndex >= model getRowCount()) { model setRowlndex(rowlndex).
D Tvles
css
Q indei* fitn.I D mde* jsp_____________________________
1 else { model.setRow!ndex(rows[rowlndex].row);
Łatwo zauważyć, że w powyższym fragmencie kodu tworzymy tablicę indeksów reprezentu jącą indeksy posortowane. Jeśli indeks przekazany na wejściu metody seiRowIndex mieści się w przedziale, wykorzystujemy indeks posortowany. Jak przebiega sam proces sortowania? Klasa SortF 1 1terModel definiuje dwie metody, sort ByF i rst () oraz sortByLasU ): public String sortByLastO { Arrays.sort(rows, byLast); return nul1;
} public String sortByFirst() { Arrays.sort(rows, byFirst); return nul1;
} Zmienne byLast i by Fi rst reprezentują komparatory. Pierwszy z nich porównuje nazwiska, drugi porównuje imiona. Implementację tych komparatorów przedstawiono na listingu 5.21 w dalszej części tego podrozdziału. Strukturę katalogów przykładowej aplikacji sortującej przedstawiono na rysunku 5.16. Na listingach od 5.20 do 5.26 przedstawiono kompletny kod źródłowy tej aplikacji.
Specyfikacja JSF 1 ,2 zaleca definiowanie co najmniej dwóch konstruktorów w kom Bfeal kretnych klasach potomnych względem klasy DataModel: konstruktora bezargumentowego wywołującego m etodę setWrappedData(nuU) oraz konstruktora przekazującego opakow ane dane do m etody setWrappedDataO. Przykład definicji tych konstruktorów m ożna znaleźć na listingu 5 .2 1 .
private static Comparator byLast = new Comparator() { public int compare(Row rl, Row r2) { Name nl = (Name) rl.getDataO; Name n2 = (Name) r2.getData(); return nl,getLast() .compareTo(n2.getLast()); } };
private static Comparator byFirst = new Comparator() { public int compare(Row rl, Row r2) { Name nl = (Name) rl.getDataO; Name n2 = (Name) r2.getData(); return n l.g etFirst() .compareTo(n2.getFirstO); } }; private class Row { private int row; public Row(int row) { this.row =row;
} public Object getDataO { int original Index = model.getRowIndexO; model.setRowIndex(row); Object thisRowData = model.getRowData(); model.setRowIndex(originalIndex), return thisRowData, } } publ i C SortFi 1terModel () { / / wymagany przez specyfikację J S F thi s( ( NameF] ) nu ll), } public SortFi 1terModel (Name[] names) { / / zalecany przez specyfikację J S F setWrappedData( names); } public SortFiIterModel(DataModel model) { this.model = model; initializeRows(); } public String sortByLastO { Arrays.sort(rows, byLast); return null; } public String sortByFirst0 { Arrays.sort(rows, byFirst), return null; } public void setRowIndex(int rowlndex) { if(rowIndex == -1 || rowlndex >= model .getRowCountO) { model.setRowIndex(rowlndex); } else { model.setRowIndex(rows[rowIndex].row); } }
I I Poniższe metody delegują wywołania bezpośrednio // do udekorowanego modelu: public boolean isRowAvailable() { return model. isRowAvai lableO ; } public int getRowCountO { return model .getRowCountO; } public Object getRowDataO { return model .getRowDataO ;
86.
}
87. 88. 89. 90. 91.
public int getRowIndexO { return model .getRowIndexO; } public Object getWrappedDataO { return model .getWrappedDataO ;
197
198
Rozdział 5. ■ Tabele danych
JavaServer Faces 92 93. 94 95. 96 97 98. 99 100
101 102 103 104. 105. 106 107 108 109.
110 111.
import ja vax.faces.model.ArrayDataModel,
public void removeDataModelListener(DataModelListener listener) { model removeDataModelListenerdistener);
4. 5 6. 7 8 9. 10. 1112. 13 14. 15. 16-
}
N.
}
18. 19.
public DataModel getNamesO { return fi 1terModel;
20.
}
}
public void setWrappedData(Object data) { model setWrappedData(data), initializeRowsO; public void addDataModelListener(DataModelListener listener) { model.addDataModelListenerdistener),
}
public DataModelListener[] getDataModelListenersO { return model.getDataModelLi sterers(),
}
private void initializeRowsO { int rowCnt = model .getRowCountO: i f (rowCnt != -1) { rows = new Row[rowCnt], for(int i=0; i < rowCnt, ++i) { rows[ i ] = new Row(i);
112
113 114. 115 }
Listing 5.22. Kod klasy sorting/src/java/conn/corejsf/Name.Java________________________ 1 package com.corejsf;
2. 3 public class Name { 4 private String first; 5 private String last; 6 private boolean markedForDeletion =false, 7. 8. public Name(String first, String last) { 9. th is .firs t = first; 10. this.last = last; 11. 12
}
13. 14 15. 16. 17. 18. 19. 20. 21
public void setFirst(String newValue) { firs t = newValue, } public String getFirstO { return firs t; }
22.
}
public void setLast(String newValue) { last = newValue; } public String getLastO { return last, } public boolean isMarkedForDeletionO { return markedForDeletion, } public void setMarkedForDeletion(boolean newValue) { markedForDeletion = newValue;
23 }
Listing 5.23. Kod klasy sorting/src/java/conn/corejsf/TableData.java 1. package com.corejsf;
2. 3 import javax.faces.model.DataModel;
public class TableData { private DataModel filterModel = nuli; private static final Name[] names = { new NameOAnna", "Kowalska"), new Name("Jan", "Winnicki"), new Name("Mariusz", "Reszczyński"), new Name("Wojciech", "Dopierała"), }. public TableDataO { filterModel = new SortFilterModel(new ArrayDataModel(names)),
21. public String deleteNamesO { 22. i f (IgetAnyNamesMarkedForDeletiorO) 23. return null; 24. 25. Named] currentNames = (Named]) fi 1terModel,getWrappedData(); 26. Named] newNames = new NamedcurrentNames length 27 - getNumberOfNamesMarkedForDeletionO] 28. 20.for(int i =0, j = 0; i < currentNames.length, ++i) { 30. Name name = (Name) currentNamesdi]; 31 i f ( ! name.isMarkedForDeletionO) { 32. newNamesdj++] = name; 33. } 34. } 35. fi 1terModel.setWrappedData( newNames); 36. return nul 1, 37. } 38. 39. public int getNumberOfNamesMarkeclForDeletionO { 40. Named] currentNames = (Named]) f i 1terModel.getWrappedDataO; 41. int cnt = 0; 42. 43. for(int i = 0; i < currentNames.length; ++i) { 44. Name name = (Name) currentNamesdi], 45. i f (name.isMarkedForDeletionO) 46. ++cnt; 47. } 48. return cnt; 49. } 50. 51. public boolean getAnyNamesMarkedForDeletionO { 52. Named] currentNames = (Named]) f i 1terModel.getWrappedDataO, 53. for(int i = 0; i < currentNames.length, ++i) { 54. Name name = (Name) currentNamesdi]; 55. i f (name isMarkedForDeletionO) 56. return true; 57. } 58. return false, 59. } 60. }
199
200
JavaServer Faces
Listing 5.24.
Plik
Rozdział 5. ■ Tabele danych konfiguracyjny soriing/web/Wt
windowTitle=Przyklad komponentu sortującego JavaBeans pageTitle=Tabli ca nazwisk: fi rstColumnHeader=Imip 1astColumnHeader=Nazwi sko deleteColumnHeaderHJsun deleteButtonText=Usuń zaznaczone nazwiska
201
M javax.faces.model.DataModel ■
mt getRowCounU) Zwraca łączną liczbę wierszy, jeśli jest znana; w przeciwnym razie zwraca wartość -1. Metoda getRowCounK ) klasy Resul tSetDataModel zawsze zwraca wartość -1.
■ Object getRowData() Zwraca dane związane z bieżącym wierszem. ■ boolean isRowAvai lableO Zwraca wartość t cue, jeśli dla indeksu bieżącego wiersza istnieją prawidłowe dane. ■
int getRowlndexO Zwraca indeks bieżącego wiersza.
■ void setRowIndex(int index) Ustawia indeks bieżącego wiersza i aktualizuje zmienną reprezentującą bieżący element kolekcji (zmienną wskazaną za pośrednictwem atrybutu var znacznika h: dataTabl e). ■ void addDataModelListener(DataModelListener listener) Dodaje obiekt nasłuchujący na modelu danych, który będzie informowany o zmianach indeksu wierszy. ■ void removeDataModelListener(DataModelListener listen er) Usuwa obiekt nasłuchujący na modelu danych. ■ void setWrappedDataCObject obj) Wskazuje obiekt, który ma być opakowywany przez model danych. ■ Object getWrappedData() Zwraca opakowane dane modelu danych.
Techniki przewijania Istnieją dwie metody przewijania zawartości tabel złożonych z dużej liczby wierszy: z w y korzystaniem paska przewijania lub za pomocą kontrolki jakiegoś innego typu, która także może zmieniać aktualną pozycję na stronie. W tym podrozdziale przeanalizujemy obie te techniki.
Przewijanie z użyciem paska przewijania Najprostszym rozwiązaniem jest przewijanie z wykorzystaniem paska przewijania. W tym celu należy umieścić znacznik h: dataTabl e w ciele znacznika di v języka H TM L:
tego rodzaju konstrukcji między innymi dla tabel tworzonych za pomocą znacznika h; da ba labie (patrz punkt Ja k prezentować wielkie zbiory danych stronami?" w rozdziale 13.). Przykład takiego rozwiązania przedstawiono na rysunku 5.18.
Rysunek 5.18.
Aplikacja przedstawiona na rysunku 5.17 różni się od aplikacji omówionej w podrozdziale „Tabele bazy danych" wyłącznie pionowym paskiem przewijania (wyświetlanym wskutek zastosowania powyższej konstrukcji HTML-a).
Rysunek 5.17. Przykład przewijania zawartości tabeli możliwego dzięki użyciu znacznika div
§
Edyąa Widok
Historia
Nsreędzia
Przewijanie za pomocą dodatkowych odwołań JSF
■ ■• ■
Przewijanie tabel bazy danych Mailla Firetov
Plik
203
Pomot
, http://localhost:8080.)
Identyfikator klienta
Imięinasińska ■Wojciech Dopierała
ul Kwiatowa 12
i wielkopolskie !
Anna K owalska Mannsz Eeszczyńskt Jan ^Jaiczorek
daiusławJabłoński ;■Krystyna Sobieraj Tomasz W ilk
,
OS: Kościuszki 3 A
^ląskift . _|
i
uł TroznańskaS? ul Falista 3
malopoUkie
iwie&opolsèàè'!
os Lecha 93/7 ul Bolesława Chtobreg.’
îV'irkre
Ę fr Przewijanie tabei bazy danych •Mpzilla Fke|ox;
p l ►! !C-i os. Ulanów8
; mazowiecki
ul Zakątek 21/2 "
;
tj
ul Gwarna 6
i' małopolskie 1
ul Kościuszki 3E
i
Wojciechowski Elżbieta M ąka
al W h kw arry 32* 1 os. Tysiąclecia 12/31
Jan Wieszczycki
ul Akacjowa 3
Franciszek Kikut.
ul Bajkowa 4/1
Ignacy Sowa
, os. Kosynierów 2A.
łódzkie ; wtefeppólskie ' wielkopolskie ' ;
tnkopolskie i
Paski przewijania są atrakcyjnym rozwiązaniem z punktu widzenia użyteczności, ale mogą się okazać zbyt kosztowne w przypadku szczególnie dużych tabel, ponieważ nie eliminują konieczności jednoczesnego ładowania wszystkich danych. Mniej wymagającą alternatywą jest użycie dodatkowych odwołań (łączy), które umożliwiają ładowanie tylko jednej, wybranej przez użytkownika strony danych.
Przewijanie za pomocą dodatkowych łączy Przewijanie dużych tabel za pomocą dodatkowych odwołań jest nie tylko bardziej efektywne niż przewijanie z wykorzystaniem paska przewijania (i znacznika di v języka H T M L), ale też dużo bardziej skomplikowane. W rozdziale 13. przeanalizujemy sposób implementowania
Aplikacja przedstawiona na rysunku 5.18 wykorzystuje tabelę danych obejmującą kody państw ISO (reprezenaijące właściwe ustawienia regionalne). Uzyskaliśmy tę listę za pośrednictwem wywołania metody statycznej java u t il Loca 1e . get ( SOCount r i es () zwracającej tablicę łańcuchów.
JavaServerFaces
6 Konwersja i weryfikacja poprawności danych W tym rozdziale omówimy sposoby konwersji danych formularzy na obiekty Javy i techniki weryfikacji poprawności uzyskiwanych w ten sposób wyników. Za realizację tych kroków przed aktualizacją modelu odpowiada kontener JS F , co daje nam pewność, że błędne (nie sprawdzone) dane wejściowe nigdy nie trafią do logiki biznesowej. W pierwszej kolejności skoncentrujemy się na ogólnej koncepcji procesu konwersji i wery fikacji poprawności. W kolejnych podrozdziałach przeanalizujemy standardowe znaczniki technologii JavaServer Faces opracowane właśnie z myślą o tych ważnych krokach. Prezen towane znaczniki powinny w zupełności wystarczyć do większości typowych zastosowań. W dalszej części tego rozdziału omówimy techniki wiązania aplikacji z własnym, niestandar dowym kodem konwertującym i weryfikującym na potrzeby bardziej złożonych scenariuszy. Istnieje też możliwość implementowania własnych znaczników reprezentujących mechani zmy konwersji i weryfikacji wielokrotnego użytku, które mogą być bez trudu konfigurowane przez autorów stron. Sama implementacja takich znaczników wymaga jednak dużo większego zaangażowania programisty. Szczegółowych informacji na ten temat należy szukać w podroz dziale „Implementacja niestandardowych mechanizmów konwersji i weryfikacji” w rozdziale 9.
Przegląd procesu konwersji i weryfikacji poprawności W tym podrozdziale dokonamy szczegółowej analizy drogi pokonywanej przez dane użyt kownika od formularza wyświetlanego w oknie przeglądarki do komponentów JayaBeans składających się na logikę biznesową.
206
JavaServer Faces Rozdział 6. ■ Konwersja i weryfikacja poprawności danych W pierwszej kolejności użytkownik wypełnia pole formularza intemeiowego. Kiedy użyt kownik klika przycisk akceptacji i wysłania formularza, przeglądarka wysyła wprowadzone dane na seiwer. Będziemy tę wartość nazywać wartością żądania (ang. request value).
Stosowanie wartości żądania
W fazie stosowania wartości żądania dane wejściowe są zapisywane w obiektach kompo nentów. (Musimy pamiętać, że dla każdego znacznika wejściowego użytego w kodzie strony JS F istnieje odpowiedni obiekt komponentu). Wartość zapisaną w obiekcie komponentu określamy mianem wartości wysianej (ang. submitted value). Wszystkie wartości żądania mają oczywiście postać łańcuchów, co jest o tyle normalne, że przeglądarka kliencka wysyła na serwer łańcuchy wpisane przez użytkownika. Z drugiej strony, aplikacja internetowa może operować na danych dowolnych typów, w tym typów int czy DaLe, a nawet najbardziej wyrafinowanych strukturach danych. Proces konwersji ma na celu przekształcenie łańcuchów przychodzących w obiekty tych typów. Szczegółowe omówienie konwersji można znaleźć w kolejnym podrozdziale. Przekonwertowane wartości nie są przekazywane bezpośrednio do komponentów JavaBeans składających się na logikę biznesową. W pierwszej kolejności są zapisywane w obiektach komponentów właściwych dla znaczników wejściowych w formie wartości lokalnych. Już po konwersji wspomniane wartości lokalne są poddawane procesowi weryfikacji poprawności. Warunki tej weryfikacji mogą być definiowane przez projektantów stron — na przykład dła niektórych pól można określić minimalną i (lub) maksymalną długość łańcucha. Do szcze gółowej analizy procesu weryfikacji poprawności przystąpimy w podrozdziale „Stosowanie standardowych mechanizmów weryfikacji” w dalszej części tego rozdziału. Po sprawdzeniu poprawności wszystkich wartości lokalnych, framework JS F przystępuje do realizacji fazy aktualizacji wartości modelu, w której wartości lokalne są umieszczane w komponentach JavaBeans (zgodnie z odpowiednimi referencjami). Część programistów zastanawia się, po co framework JS F w ogóle operuje na wartościach lokalnych. Czy nie byłoby lepiej, gdyby wartości żądania były składowane bezpośrednio w modelu? W technologii JS F zastosowano podejście dwukrokowe, które ma na celu ułatwienie zacho wywania integralności modelu. Nawet niedoświadczeni programiści doskonale wiedzą, że użytkownicy z zadziwiającą regularnością wpisują w formularzach błędne dane. Przypuśćmy, że jakieś wartości modelu zaktualizowano przed wykryciem pierwszego błędu w danych wejściowych. Model może się wówczas znaleźć w stanie niespójności, a przywrócenie orygi nalnego, prawidłowego stanu może się okazać bardzo kłopotliwe. Właśnie dlatego framework JS F w pierwszej kolejności dokonuje konwersji i weryfikacji wszystkich danych wejściowych użytkownika. W razie wykrycia błędów, strona formularza jest ponownie wyświetlana w oknie przeglądarki, stwarzając użytkownikowi możliwość uzu pełnienia lub poprawienia danych. Faza aktualizacji wartości modelu rozpoczyna się dopie ro wtedy, gdy wszystkie mechanizmy weryfikacji potwierdzą poprawność danych. Na rysunku 6.1 przedstawiono drogę pokonywaną przez wartość wpisaną w polu wejścio wym od przeglądarki internetowej do obiektu komponentu pracującego po stronie serwera i (dalej) do komponentu modelu.
207
Aktuałizacja wartości
Rysunek 6.1. Droga pokonywana przez wartość od przeglądarki internetowej do modelu
Stosowanie konwerterów standardowych W tym i kolejnym podrozdziale omówimy mechanizmy konwersji i weryfikacji poprawno ści oferowane w ramach biblioteki JS F . W dalszej części tego rozdziału skoncentrujemy się na technikach konstatowania własnych mechanizmów weryfikacji, niezbędnych w sytuacji, gdy dostępne rozwiązania nie spełniają naszych oczekiwań.
Konwersja liczb i dat Aplikacja internetowa może operować na danych wielu różnych typów, ale interfejs użyt kownika tej aplikacji oferuje wyłącznie obsługę łańcuchów. Przypuśćmy na przykład, że użytkownik ma w pewnym polu edytować datę (reprezentowaną w logice biznesowej przez obiekt klasy Date). W pierwszej kolejności istniejący obiekt klasy Date wymaga konwersji na łańcuch i wysłania do przeglądarki klienckiej celem wyświetlenia w odpowiednim polu tekstowym. Użytkownik edytuje następnie zawartość tego pola tekstowego. Wówczas łańcuch wynikowy zwrócony na serwer wymaga ponownej konwersji na obiekt klasy Date. Ta sama procedura jest stosowana — co oczywiste — także dla takich typów prostych jak int, double czy boolean. Użytkownik aplikacji internetowej edytuje łańcuchy, które są na stępnie konwertowane przez kontener JS F na typy wymagane przez logikę biznesową tej aplikacji. Aby przekonać się, jak działa konwerter wbudowany, wyobraźmy sobie aplikację internetową wykorzystywaną do przetwarzania danych o płatnościach kartą kredytową (patrz rysunek 6.2). Dane o każdej płatności obejmują: ■ wysokość płatności, ■ numer karty kredytowej, ■ datę ważności karty kredytowej.
208
Javaserver Faces
Rozdział 6. ■ Konwersja i weryfikacja poprawności danych Ęji Aplikacja testująca m
Rysunek 6.2.
£Mc
Przetwarzanie danych o płatnościach
lętycja
Jgto*
'
jrłfjton?
Zakładki
j? i
f^ałięcbia
209
Konwertery i atrybuty
Pomoc
'D- 1 ’......... ’ u
[ś, 1 W tabelach 6.1 i 6.2 wymieniono i krótko opisano konwertery oraz ich atrybuty.
Prosimy wpisać dane o płatności
Tabela 6.1. Atrybuty znacznika f:convertNumber
Kwota Karta kredytowa Data ważności (miesiąc/rok)
Atrybut
Typ
Wartość
Type
String
number (domyślnie), currency lub percentage
pattern
String
wzorzec formatowania (zgodnie z dokumentacją API klasy java.text.Decimal Format)
Dołączamy do pierwszego pola tekstowego konwerter i sygnalizujemy konieczność takiego formatowania wartości bieżącej, aby za separatorem dziesiętnym zawierała przynajmniej dwie cyfry:
Konwerter f : convertNumber jest jednym ze standardowych konwerterów oferowanych przez implementację JSF. Na tym etapie, dla drugiego pola tego formularza, nie stosujemy żadnego konwertera. (W dalszej części tego rozdziału dołączymy do niego konwerter niestandardowy). Dla trzeciego pola tekstowego użyto konwertera f :convertDateTime, a jego atrybutowi pattern przypisano łańcuch MM/yyyy. (Format łańcucha pattern opisano w dokumentacji A P I klasy java.te x t.Si mpleDateFormat).
maxFractionDigits
i nt
maksymalna liczba cyfr w części ułamkowej
minFractionDigits
int
minimalna liczba cyfr w części ułamkowej
maxintegerDigits
i nt
maksymalna liczba cyfr w części całkowitej
mirilntegerDigits
i nt
minimalna liczba cyfr w części całkowitej
integerOnly
boolean
wartość true, jeśli analizie składniowej ma być poddawana tylko część całkowita (wartością domyślną jest fal se)
groupingUsed
boolean
wartość true Je śli mają być stosowane separatory grup cyfr (wartością domyślną jest true)
locale
java.util.Locale
ustawienia regionalne, które mają zostać użyte w procesie analizy składniowej i formatowania
currencyCode
String
kod walutowy zgodnie z normą ISO 4217 wykorzystywany w procesie konwersji wartości walutowych
currencySymbol
String
symbol waluty wykorzystywany w procesie konwersji wartości walutowych
Tabela 6.2. Atrybuty znacznika f:convertDateTime Na stronie result.jsp wyświetlamy dane wejściowe podane przez użytkownika, stosując dla wysokości płatności inny konwerter niż wcześniej: < f •CO nve rtN um De r
t y p e = " c u r r e n c y " />
Tym razem nasz konwerter automatycznie dopisuje do prezentowanej wartości symbol waluty i stosuje separator cyfr (patrz rysunek 6.3).
Rysunek 6.3. Wyświetlanie informacji o płatnościach
0.
Aplikacja testująca racę
fdycja Widok- Jl«tona
Nawgtoa |
Kwota
lOOUO.OOd
Karta kredvtowa
4 1 1111 1111 11 111J 1
Data ważności (miesiącu ok) 04'200ó
Typ
Wartość
type
String
date (domyślnie), time lub both
daLeStyle
String
defaulL, short, medium, long lub ful 1
CimeS tyle
String
default, short, medium, long lub ful l
pattern
String
wzorzec formatowania (zgodnie z dokumentacją API klasy java, text .SinipleDateFormat)
locale
java.util Locale
ustawienia regionalne, które mają zostać użyte w procesie analizy składniowej i formatowania
timeZone
java.util TimeZone
strefa czasowa wykorzystywana w procesie analizy składniowej i formatowania
/'i0l.3i«o^ 8080 'eon.! »
Informacje o płatności
FyMk j
,r
Atrybut
W H Jeśli użyjemy wyrażenia reprezentującego wartość typu prostego bądź (począwszy I H i od wersji JSF 1 .2 ) typu wyliczeniowego albo typów B ig in te g e r lub BigDecim al, nie musimy stosować konwertera. Im plem entacja JSF autom atycznie wybierze odpowiedni konwerter za nas. W arto jednak pam iętać o konieczności każdorazowego wskazywania konwertera dla wartości typu Date.
210
Rozdział 6. ■ Konwersja i weryfikacja poprawności danych
JavaServer Faces
Atrybut converter Alternatywnym sposobem dołączania konwerterów do komponentów interfejsu użytkownika jest dodawanie atrybutów converter do znaczników tych komponentów. Identyfikator kon wertera można wskazać, stosując następującą konstrukcję składniową: Znaczenie tego znacznika jest identyczne jak w przypadku elementu f :convertDateTime zdefiniowanego bez żadnych atrybutów: Trzecim sposobem dołączania konwertera jest użycie następującej konstrukcji składniowej: Wszystkie implementacje JS F muszą definiować zbiór konwerterów z następującymi (pre definiowanymi) identyfikatorami: ■ javax.faces.DateTime (wykorzystywany w znaczniku f :convertDateTime); ■ javax.faces.Number (wykorzystywany w znaczniku f :convertNumber); ■ javax. faces.Boolean, javax.faces.Byte, javax.faces.Character, javax.faces.Doubl e, javax.faces.FI oat, javax.faces. Integer, javax.faces.Long, javax.faces.Short (stosowane automatycznie dla typów prostych i ich klas opakowujących); ■ javax.faces.BigDecim al, javax.faces.Biglnteger (stosowane automatycznie dla typów BigDecimal i Big Integer). W razie konieczności dodatkowe identyfikatory konwerterów można konfigurować w pliku konfiguracyjnym (więcej szczegółów można znaleźć w podpunkcie „Wskazywanie konwer terów" w dalszej części tego rozdziału).
211
Błędy konwersji W razie wystąpienia błędu konwersji zostają podjęte następujące działania: ■ Komponent, którego konwersja zakończyła się niepowodzeniem, wysyła stosowny komunikat i deklaruje swoją niepoprawność. (Sposób prezentacji tego komunikatu omówimy w dalszej części tego punktu). ■ Bezpośrednio po zakończeniu fazy weryfikacji danych, implementacja JS F ponownie wyświetla bieżącą stronę. Wyświetlona strona obejmuje wszystkie wartości wpisane wcześniej przez użytkownika (żadne dane wejściowe nie są tracone). Opisywane zachowania w większości przypadków zdają egzamin. Jeśli na przykład użyt kownik wpisze błędne dane w polu tekstowym wymagającym podania liczby całkowitej, aplikacja w żadnym razie nie powinna podejmować prób wykorzystania niewłaściwych danych wejściowych. W takim przypadku implementacja JS F automatycznie wyświetla ponownie bieżącą stronę, stwarzając użytkownikowi jeszcze jedną szansę wpisania prawi dłowych wartości. Z drugiej strony, powinniśmy unikać nadmiernie restrykcyjnych opcji konwersji dla pól
wejściowych. Przeanalizujmy przykład pola tekstowego Kwota aplikacji obsługującej płat ności. Gdybyśmy zastosowali format walutowy, wartość wpisywana w tym polu byłaby odpo wiednio formatowana. Przypuśćmy jednak, że użytkownik wpisze liczbę 100 (bez symbolu waluty). Mechanizm odpowiedzialny za formatowanie kwot wyrażonych w walutach zasy gnalizuje niezgodność otrzymanej wartości ze wzorcem. Takie podejście wydaje się zbyt restrykcyjne w przypadku wartości wpisywanych przez użytkowników. Można ten problem rozwiązać, implementując własny, niestandardowy konwerter. Konwerter niestandardowy może jednocześnie formatować wartości zgodnie z naszymi oczekiwaniami estetycznymi i akceptować niedoskonałe dane wejściowe wpisywane przez użytkowników. Zagadnienia związane z takimi konwerterami omówimy bardziej szczegółowo w podrozdziale „Programowanie z wykorzystaniem niestandardowych konwerterów i mechanizmów wery fikujących" w dalszej części tego rozdziału. Decydując się na gromadzenie danych wpisywanych przez użytkownika, powinniśmy stosować albo bardziej wyrozumiałe konwertery, albo tak przebudować formularz, aby użytkownik nie m iał wątpliwości co do oczekiwanych danych. Przykładowo: zam iast zmuszać użytkownika do podawania daty ważności karty kredytowej w formacie MM/yyyy, m ożem y um ieścić na form ularzu dwa pola w ejściowe — jedno dla m iesiąca i drugie dla roku.
□ ■ ffij Jeśli atrybutowi converter przypiszemy łańcuch, przypisana wartość będzie reprewLm zentow ała identyfikator konw ertera. Jeśli je d n a k przypiszem y tem u atrybutowi wyrażenie reprezentujące wartość, wartość tego wyrażenia będzie wymagała konwersji na obiekt (a konkretnie egzemplarz klasy implementującej interfejs Converter). Interfejs Converter zostanie omówiony w podrozdziale „Programowanie z wykorzystaniem nie standardowych konwerterów i m echanizm ów weryfikujących”.
Wyświetlanie komunikatów o błędach P | l Specyfikacja JSF 1 .2 wspom ina o atrybucie opcjonalnym binding, który może być l w i stosowany w znacznikach f :convertNumber, f :convertDateTime i f:converter. W ten sposób możemy wiązać egzemplarze konwerterów z właściwościami typu javax. ^ fa c e s . convert. Converter kom ponentu w spom agającego.
Musimy oczywiście zadbać o prezentowanie użytkownikowi komunikatów o błędach wy krywanych w procesie konwersji i weryfikacji poprawności. Wszędzie tam, gdzie stosujemy konwertery i mechanizmy weryfikujące, powinniśmy korzystać także ze znaczników h:message.
212
JavaServer Faces
Rozdział 6. ■ Konwersja i weryfikacja poprawności danych
Z reguły chcemy wyświetlać komunikaty o błędach bezpośrednio obok komponentów, któ rych te komunikaty dotyczą (patrz rysunek 6.4). Komponentowi należy nadać identyfikator, do którego można się następnie odwoływać w znaczniku h;message. Specyfikacja JS F 1.2 dodatkowo nakłada na programistę obowiązek zdefiniowania etykiety komponentu, która będzie wyświetlana w ramach komunikatu o błędzie.
Jeśli nie odwołujemy się wprost do konwertera f :convertNumber, tylko bazujemy na standardowych konwerterach dla typów liczbowych, powinniśmy wyświetlać na stronie podsum ow ania kom unikatów (zam iast kom unikatów szczegółowych). Okazuje się, że w takim przypadku kom unikaty szczegółowe są zbyt drobiazgowe. Na przykład konw erter standardow y dla w artości typu doubl e generuje kom unikaty szczegółowe w postaci: ... must be a number between 4.9E-324 and 1 .7 9 7 6 9 3 1 3 4 8 6 2 3 1 5 7E308 Example: 1 9 9 9 9 9 9 .
□
message for="amount"/>
Rysunek 6.4. Przykład wyświetlonego komunikatu o błędzie konwersji
tM m ' H
i»
^ =
ia s iA u ł . . = ^ 1 1 5 * .
213
Komunikaty o błędach z reguły są wyświetlane w kolorze innym niż standardowe opisy na stronie. Zmiana wyglądu komunikatu wymaga użycia atrybutu styleClass lub style:
Prosim y wpisać dane o płatności lub Kw ota
¡zbyt wiele
Karta kredytowa
Î 4 Ï1 Ï Ï ÏÏ 1 Ï Ï 1 1 Ï Ï Ï Ï
Data ważności (miesiąc/rok)
¡10/2005
Zaleca się stosowanie atrybutu styl eCl ass wskazującego na zewnętrzny arkusz stylów (zamiast stylu zapisanego na stałe w kodzie strony).
Zakończono
Wyświetlanie wszystkich komunikatów o błędach Programiści, którzy pracują w technologii JS F 1.1, powinni pominąć atrybut 1abel.
Komunikat może występować w dwóch wersjach: podsumowania i wersji szczegółowej.
Sytuacja, w której musimy wyświetlać wiele komunikatów dla pojedynczego komponentu, zdarza się dość rzadko, ale nie można jej wykluczyć. Znacznik h:message generuje tylko pierwszy komunikat. Nie mamy niestety pewności, czy pierwszy komunikat rzeczywiście jest najbardziej przydatny z perspektywy użytkownika. Mimo że nie istnieje znacznik w y świetlający wszystkie komunikaty dla określonego komponentu, za pośrednictwem znacznika h :messages możemy uzyskać dostęp do wszystkich komunikatów właściwych dla wszystkich komponentów interfejsu użytkownika.
W przypadku konwertera liczb, szczegółowy komunikat o błędzie powinien obejmować ety kietę odpowiedniego komponentu, odrzuconą wartość oraz przykład poprawnej wartości, np.:
Znacznik h:messages domyślnie prezentuje podsumowanie komunikatu (zamiast jego szcze gółów), a więc działa odwrotnie niż znacznik h:message.
W ramach znacznika h:message możemy definiować wiele atrybutów opisujących wygląd komunikatu (więcej szczegółów na ten temat można znaleźć w podrozdziale „Komunikaty” w rozdziale 4.). W tym podpunkcie omówimy tylko atrybuty właściwe dla komunikatów o błędach.
Wartość 'zbyt wiele' nie jest liczbą. Przykład: 99 Podsumowanie komunikatu nie obejmuje części przykładu.
Atrybutowi layout znacznika h;messages z reguły przypisujemy wartość "table", aby kolejne komunikaty były wyświetlane w kolejnych wierszach tabeli. Gdybyśmy tego nie zrobili, treść komunikatów zostałaby skonkatenowana.
Za każdym razem , gdy tworzymy kom unikat, m usim y się upewnić, że kończy się kropką i dodatkową spacją, aby w razie konkatenacji wielu kom unikatów ich treść była czytelna.
□ Znacznik h:message domyślnie wyświetla informacje szczegółowe i ukrywa podsumowanie. Jeśli chcemy, aby na stronie było prezentowane tylko podsumowanie komunikatu, powinniśmy użyć następujących atrybutów: message for=''amount" showSummary="t;rue" showDetai l="false” />
Znacznik h: messages je s t szczególnie przydatny w procesie diagnozowania opro gram owania. Za każdym razem , gdy nasza aplikacja w nieskończoność prezentuje jed n ą stronę i odrzuca kolejne żądania użytkownika, warto dodać do jej kodu znacznik , aby sprawdzić, czy faktyczną przeszkodą nie je s t błąd konwersji lub weryfikacji poprawności.
□
214
JavaServer Faces
Rozdział 6. ■ Konwersja i weryfikacja poprawności danych
'P S j W technologii JSF kom unikaty o błędach nie obejm owały etykiet. Takie podejście ■■JB znacznie ograniczało przydatność znacznika h:messages, ponieważ użytkownicy musieli sami analizować form ularz pod kątem kontrolek wejściowych, które doprowa dziły do sygnalizowanego błędu.
Stosowanie niestandardowych komunikatów o błędach Począwszy od wersji JS F 1.2, istnieje możliwość definiowania własnych, niestandardo wych komunikatów o błędach konwerterów dla poszczególnych komponentów. W tym celu należy przypisać łańcuch komunikatu atrybutowi converterMessage komponentu interfejsu użytkownika, którego wartość podlega konwersji. Przykład takiego rozwiązania przedsta wiono poniżej:
Tabela 6.3. Standardowe komunikaty o błędach konwersji Identyfíkator zasobu
Komunikat domyślny
javax faces.converter J ntegerConverter ^-INTEGER
digits.
javax faces converter 1ntegerConverter ^ 1NTEGE R_det a 1 1
¡21: "!Oi" must be a number between -2 I47483648 and 2147483647.
W przeciwieństwie do łańcuchów kom unikatów, które omówimy w kolejnym pod punkcie, łańcuchy przypisywane atrybutowi corwerterMessage są wyświetlane na stronie w niezmienionej postaci. Oznacza to, że takie symbole zastępcze jak {0} w ogóle nie są zastępow ane.
Modyfikowanie treści standardowych komunikatów o błędach W niektórych przypadkach będziemy zmuszeni zmodyfikować standardowe komunikaty o błędach konwersji wyświetlane na rozmaitych stronach naszej aplikacji internetowej. W ięk szość typowych komunikatów standardowych przedstawiono w tabeli 6.3. Łatwo zauważyć, że wszystkie klucze komunikatów szczegółowych kończą się przyrostkiem _detai 1. Aby wspomniana tabela nie zajmowała zbyt dużo miejsca, zrezygnowano z prezentacji odrębnych łańcuchów podsumowania i szczegółów w sytuacji, gdy łańcuch podsumowania jest podłańcuchem łańcucha szczegółów. W większości komunikatów symbol {0} reprezentuje nie prawidłową wartość, symbol {1} reprezentuje przykładową wartość poprawną, a symbol (2) reprezentuje etykietę komponentu. Okazuje się jednak, że w przypadku konwertera wartości logicznych etykieta komponentu jest reprezentowana przez symbol {1}. Zastąpienie komunikatu standardowego wymaga zdefiniowania pakietu komunikatów (patrz rozdział 2.). Dodanie własnego komunikatu wymaga oczywiście odwołania do właściwego klucza z tabeli 6.3. Przypuśćmy na przykład, że nie chcemy, aby konwerter f :convertNuiriber, raportując o błędzie, odwoływał się do etykiet lub przykładów prawidłowych wartości. W tym celu wystarczy umieścić w pakiecie komunikatów następującą definicję: javax.faces.converter.NumberConverter.NUMBER_detai1= ''{0 }'' nie jest liczbą.
/2/; 7 0}" must be a number consisting o f one or more
Example: /// ¡ 2 f "¡01" could not be understood as a currency value. Example: ¡11 ¡21: "¡01" could not be understood as a percentage. Example: ¡11 ¡2(( : "¡01" could not be understood as a date. Example: ¡11 ¡2j: "10/ " could not be understood as a time. Example: {1}
{!}: "{0}" must be convertible to an enum from the enum, but no enum class provided.
Musimy jeszcze ustawić nazwę bazową tego pakietu w pliku konfiguracyjnym (np. faces-
config.xml):
215
com.corejsf.nessages
216
Rozdział 6. ■ Konwersja i weryfikacja poprawności danych
JavaServer Faces Opisaną procedurę musimy stosować wyłącznie dla komunikatów, które chcemy nadpisać. W technologii JSF 1 .1 uniwersalny komunikat Conversion error occurred jest repre zentowany przez klucz javax. faces, component. Ul Input .Conversion.
Kompletny przykład konwertera Nadszedł czas na analizę naszego pierwszego, kompletnego przykładu konwertera. Strukturę katalogów przykładowej aplikacji przedstawiono na rysunku 6.5. Prezentowana aplikacja internetowa prosi użytkownika o podanie informacji o płatności (patrz listing 6.1), po czym wyświetla odpowiednio sformatowane dane na stronie potwierdzenia (patrz listing 6.2). Komu nikaty zdefiniowano na listingu 6.3, natomiast kod klasy komponentu JavaBean przedstawiono na listingu 6.4.
21 22 23
24 25 26 28 < f COnvertDateT ime pat tern-'l M M / y iy y y " / > 29 30 31 32 33 34 35.
36
Listing 6.2. Kod strony converter/web/result.jsp Rysunek 6.5.
¡¡3 co n ve rte r.w ar
Struktura katalogów aplikacji ilustrującej działanie konwertera
t Q META-INF I f
'
Q MANIFEST.MF
|33 WEB-INF
|
f
;
|
C 5 classes
\
\
\
j...................... |.. Q Paym ent Be an. cl ass
Weryfikacja długości łańcuchów i przedziałów liczbowych
Listing 6.4. Kod komponentu converter/src/java/com/corejsf/PaymentBean.java 1.
219
Stosowanie mechanizmów weryfikacji JS F w ramach stron JavaServer Faces jest bardzo proste — wystarczy w ciele znacznika komponentu interfejsu użytkownika umieścić odpo wiednie znaczniki:
package com.corejsf;
2 3. import java.util Date, 4 5. public class PaymentBean { 6. private double amount; 7 private String card = 8 private Date date = new Daten. 9
Powyższy fragment kodu dodaje do pola tekstowego mechanizm weryfikujący; w czasie wysyłania formularza, do którego należy to pole, wspomniany mechanizm sprawdza, czy wpisany łańcuch składa się z co najmniej 13 znaków. W razie niepowodzenia weryfikacji (czyli wykrycia, że dany łańcuch składa się z 12 lub mniejszej liczby znaków) zostaną wyge nerowane komunikaty o błędach związane z właściwym komponentem. Komunikaty o błę dach można następnie wyświetlać na stronie JS F za pomocą znacznika h:message lub h;messages.
13
Właściwość card:
14
//
15 16
public void setCard(Strmg newValue) { cand public String getCardO { return card. [
- newValue.
\
17 18
II Właściwość date:
19 20
public void set Dat e(Da t e newValue) j date public Date getDateO { return date. }
21
=newValue. \
}
P M | Technologia JavaServer Faces l.x nie oferuje bezpośredniej obsługi weryfikacji ■¡¡¡■1 poprawności danych wejściowych po stronie klienta. Wszystkie operacje związane z taką weryfikacją są wykonywane po stronie serwera już po wysłaniu przez użytkownika danych form ularza. Gdybyśmy chcieli przenieść te rozwiązania na poziom przeglądarki internetow ej, musielibyśm y opracować w łasne, niestandardow e znaczniki zaw ierające o dpow iednie p o lecen ia języka JavaS cript. Szczegółowych inform acji na te n te m a t należy szukać w rozdziale 1 3 . Technologia JavaServer Faces oferuje mechanizmy wbudowane, które umożliwiają nam realizację następujących zadań związanych z weryfikacją danych wejściowych:
■ sprawdzanie długości łańcuchów; ■ sprawdzanie przedziałów, do których powinny należeć wartości liczbowe (np. > 0 lub < 100); ■ sprawdzanie, czy wymagane wartości w ogóle zostały wpisane przez użytkownika.
Trudno sobie wyobrazić aplikację internetową bez jakichkolwiek mechanizmów weryfika cji danych. Ponieważ tego rodzaju mechanizmy są wszechobecne w świecie aplikacji inter netowych, należy oczekiwać, że ich stosowanie i rozbudowa nie powinny stanowić większego problemu. Okazuje się, że technologia JavaServer Faces spełnia oba te wymagania przez udostępnienie szeregu standardowych mechanizmów weryfikacji i oferowanie prostego modelu implementowania własnych, niestandardowych mechanizmów tego typu. Kluczowym zadaniem stawianym mechanizmom weryfikacji danych wejściowych jest ochrona modelu. Ponieważ w technologii JS F stosuje się odrębne etapy dla weryfikacji danych i aktuali zacji wartości modelu, możemy być pewni, że nasz model nie znajdzie się w stanie niespójno ści, nawet jeśli dane wejściowe zostaną odrzucone.
W tabeli 6.4 wymieniono standardowe mechanizmy weryfikacji oferowane przez technolo gię JSF. Przykład mechanizmu weryfikującego długość łańcucha przedstawiono na początku tego punktu. Weryfikacja numerycznych danych wejściowych wymaga użycia mechanizmu sprawdzającego przynależność do przedziału. Przykład takiego rozwiązania przedstawiono poniżej:
Atrybut required jest obsługiwany przez wszystkie znaczniki wejściowe technologii Java Server Faces. Atrybut requi red można stosować łącznie z zagnieżdżonym mechanizmem weryfikacji: <7h inputText>
M 9 Jeśli nie przypiszemy atrybutowi required wartości true i jeśli użytkownik nie wpisze l U l w odpowiednim polu wejściowym żadnych danych, nie zostaną podjęte żadne kroki weryfikacyjne! W takim przypadku puste pole zostan ie zinterpretow ane ja k o żądanie pozostawienia istniejącej wartości w niezm ienionej form ie. Alternatywna składnia dołączania mechanizmów weryfikacji do komponentów interfejsu użytkownika bazuje na znaczniku f :val idator. Identyfikator mechanizmu weryfikacji i jego parametry można zdefiniować w następujący sposób:
Jeszcze inny sposób wskazywania mechanizmów weryfikacji polega na definiowaniu atrybutu va 1idator w ramach znacznika komponentu (patrz punkt „W eryfikacja za pomocą metod komponentów JavaBeans” w dalszej części tego rozdziału).
221
1.2n zaczniki f : val i dateLength, f : val i dateLo
Wyświetlanie komunikatów o błędach weryfikacji Komunikaty o błędach weryfikacji danych wejściowych są obsługiwane w taki sam sposób jak komunikaty o błędach konwersji. Każdy taki komunikat jest dodawany do komponentu, który nie przeszedł procesu weryfikacji. Odpowiedni komponent interfejsu użytkownika jest wówczas oznaczany jako nieprawidłowy, a bieżąca strona jest ponownie wyświetlana bezpośrednio po zakończeniu fazy weryfikacji procesu. Do wyświetlania komunikatów o błędach weryfikacji służą znaczniki h :message i h . messages. Szczegółowe omówienie tych znaczników można znaleźć w podpunkcie „Wyświetlanie komu nikatów o błędach” we wcześniejszej części tego rozdziału. Technologia JS F 1.2 oferuje nam możliwość definiowania komunikatów niestandardowych dla komponentów interfejsu użytkownika za pośrednictwem atrybutu requi redMessage lub validatorMessage: requiredMessage="#{msgs.cardRequired}" validatorMessage="#{msgs.cardInvalid}">
W tym punkcie przeanalizujemy przykładową aplikację prezentującą formularz, dla którego zastosowano wszystkie standardowe mechanizmy weryfikacji technologii JSF : pola wymagane, łańcuchy określonej długości oraz przedziały liczbowe. Prezentowana aplikacja sprawdza, czy użytkownik wypełnił wszystkie pola, czy kwota płatności mieści się w przedziale od 10 do 10 000, czy numer karty kredytowej obejmuje co najmniej 13 znaków i czy numer PIN jest liczbą z przedziału od 1000 do 9999. Typowe komunikaty o błędach weryfikacji przed stawiono na rysunku 6.6. Nasz formularz zawiera też przycisk Anuluj , którego kliknięcie powoduje pominięcie procesu weryfikacji.
Rysunek 6.6.
I
Typowe komunikaty o błędach weryfikacji danych wejściowych
Plik
Edycja
W idok
Historia
Zakładki
Naizęttzia
| \
P om oc
http://locaIhost:8U80/yali dator/mdex.fact|J.jpk] [j C ^ ^ o g le
P rosim y w p isać dane o płatności — •>Kwola tiąU v.rrJ>ytik.,icv'. lYrekazany •¡irTut mc- mtś> i
{I}: Validation Error: Value Obiekty klas DoubleRangeVal idator i LongRangeValidator w sytuacji, is less than allowable gdy podana wartość nie mieści się minimum of"{0 w przedziale i gdy zdefiniowano tylko wartość minimalną. {0}: Validation Error: Value Obiekty klas DoubleRangeVal idator is not o f the correct type. i LongRangeVal idator w sytuacji, gdy konwersja danej wartości na liczbę typu doubl e lub 1ong jest niemożliwa. {1}: Validation Error: Value Obiekt klasy LengthVal idator w sytuacji, gdy długość łańcucha is greater than allowable wejściowego jest większa maximum of"{0 }”. od długości maksymalnej.
1maietssa
a
cyh
i Data
.1
tvaai->$ci •untsiĄchck} pj.f w^r-rnkacjt Bnik ' wvirtv .¿ci v
Strukturę katalogów tej aplikacji przedstawiono na rysunku 6.7. Na listingu 6.5 przedsta wiono kod strony JS F , w którym odwołano się do mechanizmów weryfikacji. 3
Rysunek 6.7. Struktura katalogów przykładowej aplikacji
v a lid a to r , w a r
f- S3 META-IMF j [ Q MANIFEST.MF f- E3 WEB-INF
j f- (¡3 c l a s s e s | |
| |
f E 3 com | 7 Q ' D
{!}: Validation Error: Value Obiekt klasy LengthVal i dator w sytuacji, gdy długość łańcucha is less than allowable wejściowego jest mniejsza minimum o f "{0} ". od długości minimalnej.
t ( ¡3 c o r e js f )-” Q P a y m e n t B e a n . cla s s G m e s s a g e s .p r o p e r t i e s f a c e s - c o n f i g .x m l w eb.xm l
■Q styles, css ~ Q index, htrnl r- Q canceled.jsp
Oczekiwanie od użytkowników wypełniania pól wymaganych nawet wtedy, gdy mogą się decydować na anulowanie całego formularza, byłoby nielogiczne. Okazuje się, że istnieje mechanizm umożliwiający pomijanie procesu weryfikacji. Jeśli przypiszemy wartość true atrybutowi immediate kontrolki polecenia, odpowiednie polecenie zostanie wykonane w fazie stosowania wartości żądania. Przycisk Anuluj można by zaimplementować w następujący sposób: 3. taglib uri="http7/java.sun.com/jsf/html" prefix="h" %> 4. 5. 6. 8.
224
Rozdział 6. ■ Konwersja i weryfikacja poprawności danych
Implementacja klas konwerterów niestandardowych Konwerter ma postać klasy odpowiedzialnej za konwersję danych pomiędzy postacią łańcu chową a odpowiednimi obiektami. Każdy konwerter musi implementować interfejs Converter obejmujący następujące metody: Object getAsObject(FacesContext context, UlComponent component, String newValue) String getAsString(FacesContext context. UlComponent component, Object value)
Pierwsza metoda konwertuje łańcuch na obiekt odpowiedniego typu i generuje wyjątek ConverterException w sytuacji, gdy taka konwersja okaże się niemożliwa. Metoda getAsObject jest wywoływana w chwili wysłania przez klienta danych w formie łańcucha (zwykle jest to wartość wpisana w polu tekstowym). Metoda getAsString konwertuje istniejący obiekt na re prezentację łańcuchową, która może być wyświetlana za pośrednictwem interfejsu klienckiego. Aby przekonać się, jak w praktyce działają te metody, spróbujemy opracować konwerter niestandardowy dla numerów kart kredytowych. Nasz konwerter umożliwi użytkownikom wpisywanie numerów zarówno ze spacjami, jak i bez spacji. Oznacza to, że będą akcepto wane dane wejściowe w następujących formatach: 1234567890123456 1234 5678 9012 3456
Kod tego konwertera przedstawiono na listingu 6.6. Metoda getAsObject klasy naszego kon wertera eliminuje z łańcucha wejściowego wszystkie znaki inne niż cyfry, po czym tworzy obiekt klasy CreditCard. W razie wykrycia błędu opisywany konwerter konstruuje obiekt klasy FacesMessage i generuje wyjątek ConverterException. Szczegółowe omówienie tych kroków można znaleźć w podpunkcie „Raportowanie o błędach konwersji” w dalszej części tego podrozdziału.
Listing 6.6. Kod konwertera con verter2/src/ja va/com/coresjf/CreditCardCon verierja va_______________ 1. package com.corejsf,
Programowanie z wykorzystaniem niestandardowych konwerterów i mechanizmów weryfikujących Standardowe konwertery i mechanizmy weryfikacji technologii JS F okazują się wystarczające w przypadku większości zastosowań, jednak wiele aplikacji internetowych wymaga bardziej zaawansowanych rozwiązań. Możemy na przykład stanąć przed koniecznością konwersji typów innych niż liczby czy daty lub przeprowadzania procesu weryfikacji właściwego dla budowanej aplikacji, np. dotyczącego karty kredytowej. W kolejnych punktach tego podrozdziału omówimy sposób implementacji niestandardowych, własnych konwerterów i mechanizmów w eryfikacji z m yślą o konkretnych aplikacjach. Implementacja tego rodzaju rozwiązań wymaga od programisty sporo pracy.
public class CreditCardConverter implements Converter { public Object getAsObject(FacesContext context, UlComponent component, String newValue) throws ConverterException { StringBuilder builder = new StringBuilder(newValue); boolean foundlnvalidCharacter= false; char invalidCharacter = 707 int i = 0; while (i < builder.lengthO && !foundlnvalidCharacter) { char ch = bui1der.charAt( i ); i f (Character.isDigit(ch)) i++; else i f (Character.isWhitespace(ch)) builder,deleteCharAt(i); else {
226
JavaServer Faces 23. 24. 25. 26 27 28 29 30.
foundlnvalidCharacter = true; invalidCharacter = ch; } i f (foundlnvalidCharacter) { FacesMessage message = com.corejsf.uti1.Messages.getMessage( "com.corejsf.messages", "badCreditCardCharacter", new Object[]{ new Character(invalidCharacter) }). message.setSeveri ty(FacesMessage.SEVERITY_ERROR), throw new ConverterException(message); }
32
33 .
43 .
44. 45. 46. 47. 48 49 50 51. 52. 53 54.
return new CreditCard(builder.toStringO); } public String getAsString(FacesContext context, UlComponent component, Object value) throws ConverterException { I I długość: 13 cyfr — xxxx xxx xxx xxx I I długość: 14 cyfr — xxxxx xxxx xxxxx I I długość: 15 c y fr — x x xx x xxxxx x x xxx / / długość: 16 cyfr — xx xx x x xx x x xx xx xx I I długość: 22 cyfry — xxxxxx xxxxxx xx xxxxxxxx
58 59. 60 61 62. 63. 64 65. 66. 67.
String v = value.toStringO ; in t[] boundaries = null; int length = v lengthO; 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) boundaries = new in t[]{ 4, 8 , 12 }; else i f (length ==22 ) boundaries = new in t[]{ 6, 14 }; else return v; StringBuiIder result = new StringBuilder(); int start = 0; for (int i = 0; i < boundaries length; i++) { int end = boundaries[i]; result.append(v.substring(start, end)); result.appendC "); start = end;
Metoda getAsStri ng klasy konwertera odpowiada za formatowanie numeru karty kredytowej w sposób zgodny z oczekiwaniami użytkownika. Cyfry pogrupowano zgodnie ze wzorcami właściwymi dla poszczególnych rodzajów kart kredytowych. Formaty numerów najbardziej popularnych rodzajów kart kredytowych przedstawiono w tabeli 6.6.
227
Tabela 6.6. Formaty numerów kart kredytowych Rodzaj Karty
Liczba cyfr
Format
MasterCard
16
5xxx xxxx xxxx xxxx
Visa
16
4xxx xxxx xxxx xxxx
Visa
13
4xxx X X X
Discover
16
6xxx xxxx xxxx xxxx
American Express
15
31XX xxxxxx xxxxx
American Express
22
3xxxxx xxxxxxxx xxxxxxxx
Diners Club, Carte Blanche
14
3xxxx xxxx xxxxx
}
31
34 35 36 37 38. 39. 40. 41. 42.
Rozdział 6. ■ Konwersja i weryfikacja poprawności danych
X XX X XX
W analizowanym przykładzie klasa CreditCard ma charakter pomocniczy, a jej rola ogranicza się do reprezentowania numeru karty kredytowej (patrz listing 6.7). Moglibyśmy oczywiście pozostawić oryginalną reprezentację numeru karty kredytowej w formie obiektu klasy S tri ng, a tym samym ograniczyć zakres funkcjonowania konwertera do zadań właściwych raczej dla formatera. Z drugiej strony, większość konwerterów ma na celu przekształcanie oryginalnych danych wejściowych w obiekty typów innych niż String. Aby uprościć ewentualne wyko rzystywanie prezentowanego przykładu w innych aplikacjach, zdecydowaliśmy się zdefi niować wyspecjalizowany typ docelowy.
Listing 6.7. Kod klasy converter2/src/java/com/corejsf/CreditCard.java___________________ 1. package com.corejsf;
2 3. public class CreditCard { 4. private String number; 5. 6. public CreditCard(String number) { this.number = number; } 7. public String toStringO { return number; }
8- }
Wskazywanie konwerterów Jednym ze sposobów wskazywania konwerterów jest korzystanie z symbolicznych identy fikatorów rejestrowanych za pośrednictwem aplikacji JSF. Dla naszego konwertera numerów kart kredytowych wykorzystamy identyfikator com.corej s f .Credi tCard. Poniżej przedstawiono wpis umieszczony w pliku konfiguracyjnym faces-config.xml i wiążący wspomniany iden tyfikator konwertera z klasą, która ten konwerter implementuje: com.corejsf.CreditCardcom.corejsf.CreditCardConverter
Od tej pory przyjmujemy, że właściwość card komponentu PaymentBean reprezentuje obiekt klasy CreditCard (patrz listing 6.13 w dalszej części tego podrozdziału). Możemy teraz wyko rzystać znacznik f .-converter do określenia identyfikatora konwertera:
Możemy też użyć bardziej zwięzłej konstrukcji składniowej w postaci atrybutu Converter:
Konwertuje daną wartość łańcuchową na obiekt, który może być składowany w ramach danego komponentu.
W tym konkretnym przypadku komponent JavaBean nazwany bb musi obejmować właści wość convert typu Converter. W razie konieczności możemy zaimplementować metodę zwracającą wartość tej właściwości, czyli obiekt klasy wewnętrznej:
Raportowanie błędów konwersji Po wykryciu błędu, konwerter powinien wygenerować wyjątek typu ConverterException. Na przykład metoda getAsObject naszego konwertera numerów kart kredytowych sprawdza, czy wpisany numer nie zawiera znaków innych niż cyfry i separatory. W razie wykrycia niewłaściwego znaku wspomniana metoda sygnalizuje wystąpienie błędu:
public class BacKingBean { pub11 c Converter getConvertO { r e t u r n new Convertern { puPhc Object getAsObject(FacesContext context. UlComponent component. String newValue) tnrows ConverterException { } public String getAsStrmgtFacesContext context. UlComponent component. Object value) tnrows ConverterException { }
f }
if (foundlnvalidCharacter) { FacesMessage message = new FacesMessage( "Wystąpił błąd Konwersji ". "Błędny numer K a r t y message.setSeventy(FacesMessage.SEVERltY_ERR0R). throw new ConverterException(message).
K re d y to w e j."
).
} Obiekt klasy FacesMessage reprezentuje podsumowania i szczegóły komunikatów, które można wyświetlić za pośrednictwem odpowiednich znaczników.
} javax. faces.appli cati on.FacesMessage
Prezentowane podejście jest o tyle wygodne, że umożliwia metodom odpowiedzialnym za konwersję uzyskiwanie dostępu do danych prywatnych komponentu JavaBean.
■ FacesMessage(FacesMessage.Seventy severity. String summary. String d etail) Konstruuje komunikat na podstawie przekazanej na wejściu wagi (poziom dotkliwości), podsumowania i szczegółowego opisu. Za pośrednictwem parametru wagi (seven ty) możemy przekazać stałą SEVER1 TY_ERR0R, SEVERlTY_FATAL, SEVER!TY_LNF0 lub SEVER1 TY_WARN klasy FacesMessage.
Alternatywnym rozwiązaniem jest zarejestrowanie konwertera w roli konwertera domyślnego dla klasy CreditCard (warunkiem takiego podejścia jest pewność co do możliwości jego wykorzystywania do wszystkich konwersji pomiędzy obiektami klas String i CreditCard):
■ FacesMessage(Strmg summary. String d etail)
com corejsf CreduCardcom.corejsf DechtCardConverter
Konstruuje komunikat oznaczony wagą SEVERITYJMFO i reprezentujący przekazane na wejściu podsumowanie oraz szczegółowy opis.
Od tej pory nie musimy nawet wspominać o tym konwerterze w innych miejscach. Nasz konwerter będzie bowiem wykorzystywany automatycznie wszędzie tam, gdzie zostanie wykryta referencja do wartości typu CreditCard. Przeanalizujmy na przykład następujący znacznik:
■ void setSeverity(EacesMessage.Seventy severity) Ustawia określoną wagę komunikatu o błędzie. Waga może być reprezentowana przez stałą SEVERITY JRROR, SEVER1 T Y J ATAL, SEVERl TYJNFO lub SEVER1TY_WARN klasy FacesMessage.
com.mycompany.myapp.messages
Poniżej przedstawiono fragment kodu uzyskujący nazwę pakietu komunikatów: Application app = context.getApplication(); String appBundleName = app.getResourceBundle();
Przed odwołaniem się do komunikatów domyślnych powinniśmy przeszukać zasoby zawarte w tym pakiecie. I wreszcie możemy podjąć decyzję o zdefiniowaniu komunikatów obejmujących szczegółowe informacje o charakterze błędów. Możemy na przykład zasugerować użytkownikowi, który znak w numerze karty kredytowej spowodował jego odrzucenie. Okazuje się, że łańcuchy komunikatów mogą obejmować symbole zastępcze {0 }, {1} itd. Poniżej przedstawiono prosty przykład: Podany numer karty kredytowej zawiera niewłaściwy znak: {0}.
Klasę java .text .MessageFormat można wykorzystać do zastąpienia tych symboli właściwymi wartościami: Object[] pa rams = MessageFormat formatter = new MessageFormat(resource, locale), String message = formatter format(params);
3. Uzyskanie dostępu do pakietu komunikatów właściwego dla danej nazwy, ustawień regionalnych i mechanizmu ładowania klas: ResourceBundle bundle = ResourceBundle.getBundle(bundleName, locale, loader):
4. Uzyskanie ze wspomnianego pakietu komunikatów łańcucha zasobu właściwych dla danego identyfikatora: String resource = bundle.getString(resourceld),
Przedstawiony proces nie jest wolny od wad. Jego realizacja wymaga istnienia dwóch ko munikatów: podsumowania komunikatu i komunikatu szczegółowego. Zgodnie z konwen cją identyfikator zasobu właściwy dla komunikatu szczegółowego uzyskujemy przez doda nie do klucza podsumowania przyrostka _detai 1. Poniżej przedstawiono prosty przykład: badCreditCardCharacter=Błędny numer karty kredytowej. badCreditCardCharacter_detail=Podany numer karty kredytowej zawiera niewłaściwe znaki.
W przedstawionym przykładzie tablica params zawiera wartości, które mają zostać użyte w miejsce symboli zastępczych. (W ięcej informacji o klasie MessageFormat można znaleźć w rozdziale 10. książki Core Java™ 2, vol. 2 — Advanced Features (7th ed.)x autorstwa Caya Horstmanna i Gary’ego Cornelia). Idealnym rozwiązaniem byłoby przeniesienie odpowiedzialności za realizację tych zadań na poziom frameworku JSF. Mimo że odpowiedni kod można znałeźć wewnątrz implementacji referencyjnej, projektanci tego frameworku nie zdecydowali się na udostępnienie tego mecha nizmu programistom JSF .
1 Polskie wydanie: Java 2. Techniki zaawansowane. Wydanie //, Helion, 2005 — przyp. tłum.
232
JavaServer Faces Rozdział 6. ■ Konwersja i weryfikacja poprawności danych Opracowaliśmy pakiet com.corejsf.uti 1 obejmujący klasy pomocnicze, które implementują brakujące elementy funkcjonalności. Zachęcamy programistów do swobodnego korzystania z tych rozwiązań w kodzie budowanych aplikacji. Klasa com.co rejsf.u til .Messages obejmuje metodę statycznągetMessage, która zwraca obiekt klasy FacesMessage reprezentujący określoną nazwę pakietu komunikatów, identyfikator zasobu i dodatkowe parametry: FacesMessage message = com.corejsf u ti1.Messages,getMessage( "com.corejsf.messages", "badCreditCardCharacter". new Object[] { new Character(invalidCharacter) }),
Jeśli dany komunikat nie obejmuje symboli zastępczych, zamiast tablicy parametrów po winniśmy przekazać wartość nuli. Nasza implementacja jest zgodna z konwencją obowiązującą w świecie JSF , gdzie brakujące zasoby są zastępowane łańcuchem r?.V.identyfikator_zasobuRV.. Kod źródłowy klasy Messages przedstawiono na listingu 6.8.
Listing 6.8. Kod klasy converter2/src/java/com/corejsf/util/Messages.java 1. package com.corejsf. u t il,
i f (bundlel != riul 1) { bundle = ResourceBundle.getBundle(bundlel. locale, loader), i f (bundle != riul 1) try { resource = bundle.getString(resourceld), } catch (MissingResourceException ex) { } } i f (resource == nul1) { bundle = ResourceBundle.getBundle(bundle2, locale, loader), i f (bundle != nul1) try { resource = bundle.getString(resourceld), } catch (MissingResourceException ex) { } }
62. 63. 64.
i f (resource == null) return null; // brakdopasowania i f (params == null) return resource,
65. 66 67 68.
}
MessageFormat formatter = new MessageFormat(resource, locale); return formatter.format(params);
69
public static Locale getLocale(FacesContext context) f
70
Lo cale loca 1e = n u l 1.
71
UlViewRoot viewRoot - context getViewRooti ).
72 73 74 75 76
i f (viewRoot != n u l l ) loc a le - viewRoot g e t L o c a le ( ) . i f ( l o c a l e == n u l l ) l o c a le = Lo cale g e t D e f a u I t ( ) re tu rn l o c a le . f
77
public s t a t i c ClassLoader getC 1a s s L o a d e r() j
78 79 80 81 82
ClassLoader loader - thread c u r r e n t t h r e a d ( ) g e tC o n te xtC las sLo a d ert ). i f (l o ad e r == n u l l ) loader - ClassLoader g et Sy st em Class Lo ader( ). retu rn loader. } }
H H Program iści, którzy preferują stosow anie standardowych kom unikatów JSF dla K w i błędów konwersji, mogą wywołać następującą m etodę:
Rozdział 6. ■ Konwersja i weryfikacja poprawności danych
JavaServer Faces
m t i e x h outputText va lue="#{msgs t u i e [ "/>
7
25] ja vax.faces.context.FacesContext
8 9
■ static FacesContext getCurrentinstance!) Zwraca kontekst żądania obsługiwanego przez bieżący wątek lub wartość nul 1, jeśli wątek bieżący nie obsługuje żadnego żądania. ■ UlViewRoot getViewRoot() Zwraca komponent główny właściwy dla żądania opisywanego przez dany kontekst.
10
11 12 13
M 15 16 17
18
EHI javax. faces.component.UlViewRoot
19
■ Locale getLocaleO Zwraca ustawienia regionalne właściwe dla danego widoku.
Przykładowa aplikacja zbudowana na bazie konwertera niestandardowego W tym podpunkcie przeanalizujemy pozostałe elementy naszej kolejnej aplikacji przykładowej. Na rysunku 6.8 przedstawiono strukturę katalogów tej aplikacji. Na listingach 6.9 i 6.10 przed stawiono odpowiednio kod strony z formularzem wejściowym i kod strony wynikowej. Warto zwrócić uwagę na znaczniki inputText i outputText reprezentujące numery kart kredytowych, w których stosujemy dwa style wskazywania naszego konwertera niestandardowego. (Obie konstrukcje można by pominąć, gdybyśmy zarejestrowali ten konwerter jako konwerter domyślny typu CreditCard). ^ converters.war
Struktura katalogów aplikacji ilustrującej działanie niestandardoweg o konwertera
f~ £3 META-l WF I
f ifsl Util L D Messages.class
j
\
j
|
i-
\
\
\
1 I
r
Q CreditCard. class Q
CreditCardConverter.class
D PaymentBean. class -- Q messages, properties
! I
21 23 24 25. 26. 27 28. 30.
20
Rysunek 6.8.
form>
M COnvertNumper minFractlOnDigits=,2"/>
| Q faces-config.xmt Q web.xml
U D styles, css L- Q index.html I- Q index.jsp □ result.jsp__________ ___________________
< h : o u t p u t T e x t v a l u e = " # { m s g s . e x p i r a t i o n D a t e } "/>
235
236
Rozdział 6. ■ Konwersja i weryfikacja poprawności danych
JavaServer Faces 23. 24 25. 26 27 28 29. 30 31
39. 40. com.corejsf.messages 41 msgs 42. 43. 44.
Listing 6.12. Plik właściwości converter2/src/java/com/corejsf/messages.properties 1. badCreditCardCharacter=Błędny numer karty kredytowej. 2. badCreditCardCharacter_detail=Podany numer karty kredytowej zawiera niewłaściwy znak. {0}. 3 title=Aplikacja testująca mechanizm konwersji danych 4. enterPayment=Prosimy wpisać informacje o płatności 5. amount=Kwota 6 creditCard=Karta kredytowa 7 expirationDate=Data ważności (miesiąc/rok) 8 process=Przetwórz 9. paymentInformation=Informacje o płatnościach
Konwerter niestandardowy zdefiniowano w pliku konfiguracyjnym faces-config.xml (patrz listing 6.11). W pliku messages.properties (patrz listing 6.12) zdefiniowano komunikaty o błędach konwertera numerów kart kredytowych. I wreszcie na listingu 6.13 przedstawiono komponent JavaBean obsługujący płatności i definiujący trzy właściwości typów double, Date i CreditCard.
xsi :schemal_ocation="http://ja v a .sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_l_2.xsd" version="1.2"> com.corejsf.messages
/i ndex.jsp process/re su lt.jsp/result.jspback/i ndex.jspcom.c o re jsf.Credi tCardcom.corejsf.CreditCardConverterpayment com.corejsf.PaymentBeansession
237
package com.corejsf, import java.util.Date; public class PaymentBean { private double amount; private CreditCard card = new CreditCardC"); private Date date = new DateO, / / W łaściwość amount: public void setAmount(double newValue) { amount = newValue; } public double getAmountO { return amount, }
/ / W łaściwość card: public void setCardCCreditCard newValue) { card = newValue; } public CreditCard getCardO { return card; } / / W łaściwość date:
public void setDate(Date newValue) { date = newValue; } public Date getDateO { return date; } }
Implementacja klas niestandardowych mechanizmów weryfikacji Implementacja własnych, niestandardowych klas mechanizmów weryfikacji jest procesem dwuetapowym (podobnym do tego, który analizowaliśmy we wcześniejszej części rozdziału): 1.
Implementacja mechanizmu weryfikacji w formie klasy implementującej interfejs javax.faces.v a lidator.Validator.
2. Rejestracja tego mechanizmu w pliku konfiguracyjnym (np.faces-config.xml).
238
JavaServer Faces
Rozdział 6. ■ Konwersja i weryfikacja poprawności danych
Interfejs Val idator definiuje tylko jedną metodę:
Jeśli proces weryfikacji zakończy się błędem, metoda val i date powinna utworzyć obiekt klasy FacesMessage opisujący ten błąd, a następnie utworzyć i rzucić wyjątek Val i datorExcepti on: if
( .walidacja s i ę n i e p o w i e d z i e ) {
FacesMessage message = ; message.setSeveri ty ( FacesMessage.SEVERITY_ERROR): throw new ValidatorException(message).
} Opisany proces bardzo przypomina procedurę raportowania błędów konwersji, z tą różnicą, że generuje wyjątek Val i datorExcepti on (zamiast wyjątku ConverterException).
1f (i > 0) { int d = 2 * Integer parseInt(cardNumber.substring(i - 1, i) ) . if(d > 9) d -= 9; sum += d,
} } return sum % 10 == 0;
} private static String getDigitsOnly(String s) { StringBuiIder digitsOnly = new StringBuiIder (), char c, for(int i = 0, i < s.length (), i++) { C = S charAt
47
Na listingu 6.14 przedstawiono mechanizm weryfikacji opracowany z myślą o cyfrach nu meru karty kredytowej i zbudowany na bazie wzoru Luhna. Na rysunku 6.9 przedstawiono stronę tej przykładowej aplikacji w działaniu. Zgodnie z konwencją opisaną w podpunkcie „Uzyskiwanie dostępu do komunikatów o błędach zapisanych w pakiecie komunikatów” wykorzystujemy do lokalizowania łańcuchów komunikatów w pakiecie komunikatów klasę pomocniczą com. corejsf u til Messages.
Listing 6.14. Kod klasy validator2/src/java/com/corejsf/CreditCardValidatorjava______________________ 1
package com c o r e j s f
2 3 4 5 6 7 o 0 9 10
import import import import
javax javax javax javax
faces faces faces faces
appiicatio n.F ac es M es sag e. component UlComponent. c o n tex t.F a c esC o n tex t. va lid a to r v a iid ato r.
import javax faces v a l i d a t o r va i id a to r E x c e p ti o n . publ ic c la s s Cre duCardVal ida to r
implements V a li d a t o r j
p u blic void v a l idate(FacesConte xt context . UlComponent component Object va lue ) { lue == null ) re turn . S t r in g cardhumber.
11 12
1 f(va
13 14
if
(v alu e mstanceo f C re du C a rd ) cardhumber = value t o S t n n g ( ) .
15
el s e cardhumber = g e t D i g u s O n ly t v a l u e t o S t r m g u ) . i f ( ! luhnCheckicardhumber)) {
16 17 18
21
FacesMessage message = com.corejsf u t i l Messages. getMessage( "com c o r e j s f messages". "badLuhnCheck". null ).
22
message se tSe ve rity(F ace sM es sa ge SEV ERirv_ERR0R)
19 20
26 27 28
1 I p r i v a t e s t a t i c boolean luh nCheck(Stnng cardhumber) { int sum = 0
29 30. 31
(i)
(C haracter
i s D i g m c ))
c h g ils O n ly
append(c).
\
\ \ return cHgitsOnly to Strin g ( ) .
|M
Rysunek 6.9.
Aplikacja testująca
Przykład błędu wykrytego z wykorzystaniem algorytmu Luhna
i
£•»*
Wycja
v
Widok
jmUSiMEM j
-ttm ija Htrtona
Zakładki
Narzędzia
Pomoc
Al fc c is
*|§f typ ||J [. ttp•''.Qcaiho«"i:iOfiii/vail.iato^rnide...idcej *
j P rosim y w pisać dane o płatności 1 Kwmta i Karta k rn h to w a
n000n
i*»«««“ 11 imiesiąaroki k] i Ptąełwętri ]
iM
i l 1l i n n f n 111?
~~.......... i (i
; 1 •'
•* " -
•»
||l Zakończono ;j ^ j Wzór Luhna (opracowany przez grupę m atem atyków w drugiej połowie lat sześć“ dziesiątych ubiegłego wieku) umożliwia weryfikację i generowanie zarówno numerów kart kredytowych, ja k i num erów ubezpieczeń społecznych stosowanych w Kanadzie. Stosując ten wzór, możemy łatwo sprawdzić, czy podana sekwencja cyfr je s t prawidłowa i czy dwie cyfry nie zostały przestawione. Więcej informacji o tym wzorze można znaleźć n a stronie internetow ej http://www.merriampark.com/anatomycc.htm. Na potrzeby dalszych eksperymentów warto podkreślić, że numer 41111111 l i i i i m je s t zgodny z tym schem atem .
throw new Va 1id a t o rE x c e p t îon(message).
23 24 25
48 49 50 51 52 53
i f
fo r ( i n t i = carclNumber.lengthO - 1: i >=0; i -= 2) { sum += In tege r p a r s e In t( c a r d N u m b e r , s u b s tr i n g (i , i + 1 ) ) ;
QQQjavax. faces, val idator. Validator ■ void v a lidate(FacesContext context. UlComponent component. Object value) Weryfikuje komponent, do którego dołączono dany mechanizm weryfikacji. W razie wystąpienia błędu weryfikacji generuje wyjątek Va 11datorExcepti on.
239
240
üavaServer Faces
Rozdział 6. ■ Konwersja i weryfikacja poprawności danych
Rejestrowanie wtasnych mechanizmów weryfikacji Skoro dysponujemy już klasą mechanizmu weryfikacji, możemy przystąpić do jego rejestracji w pliku konfiguracyjnym (np. faces-config.xml). Można to zrobić, stosując następującą kon strukcję składniową:
Rysunek 6.10. Struktura katalogów aplikacji przykładowej
¡3 vaiidator2.war f a META-INF j L Q MANIFEST.MF f 1 ¡¡a WEB-INF | F S3 classes ; j F £3 com j i F- d corejsf ! |
Do niestandardowych mechanizmów weryfikacji możemy się odwoływać za pośrednictwem znacznika f : val i dator — w poniższym fragmencie kodu wykorzystujemy omówiony przed chwilą mechanizm weryfikacji numerów kart kredytowych:
241
\ j
Ÿ" ESI Util j '■■■■-D Messages.class
|
|
j Q
i
\
| D CreditCardConverter. class
CredltCard. class
j
|
| O CreditCardValldator. class
\
|
|
j
|
'■... D messages, properties
Q PaymentBean. class
|....|.. Q faces-config.xml O web xml i Q styles css ; D index htmi
| D index jsp • D result jsp_____________________________
Wartość atrybutu valid ato rld użyta w znaczniku f : validator musi odpowiadać identyfi katorowi mechanizmu weryfikacji zdefiniowanemu w pliku konfiguracyjnym. Znacznik f-val i dator wykorzystuje ten identyfikator do odnalezienia właściwej klasy, utworzenia jej egzemplarza (w razie konieczności) i wywołania metody val idate. R H W technologii JSF stosuje się odrębne przestrzenie nazw d la identyfikatorów l w i konwerterów i m echanizm ów weryfikacji. Oznacza to, że wykorzystywanie identy fikatora corn. corejsf. CreditCard zarówno dla konwertera J a k i mechanizm u weryfikacji nie stanowi żadnego problemu.
.
■
.
Framework JSF rejestruje trzy standardowe mechanizmy weryfikacji z następującymi identyfikatoram i: ja'vax.faces.LongRange, javax.faces.DoubleRange i javax.
^faces. Length. Pozostałe elementy naszej przykładowej aplikacji są już bardzo proste. Na rysunku 6.10 przed stawiono strukturę jej katalogów, natomiast na listingu 6.15 przedstawiono kod odpowiedniej strony JSF.
<1-conventNumber minFractlonDigilS="2"/>
Miesiąc R c.y
¡2006
,
....
. • .i Ł.~; ,
.
['Wyślij j
Przekazywanie konwerterom atrybutów Każdy komponent interfejsu użytkownika JS F może obejmować dowolną liczbę atrybutów. Atrybuty komponentów możemy definiować za pomocą znaczników f ■attribute. Istnieje też możliwość ustawiania atrybutów dołączanych do konwertera. Nasz konwerter może następnie uzyskiwać dostęp do tak zdefiniowanych atrybutów za pośrednictwem odpowied niego komponentu. W tyra punkcie przeanalizujemy przykład wykorzystania atrybutów do zdefiniowania łańcucha separatora dla konwertera numerów kart kredytowych. Dołączając konwerter, należy zagnieździć znacznik f : a ttri bute w ramach znacznika kom ponentu:
Jeśli użytkownik wpisze błędną datę, np. 30 lutego, nasza aplikacja powinna wygenerować błąd weryfikacji i uniemożliwić przekazanie tych danych do modelu. Rozwiązaniem tego problemu jest przypisanie mechanizmu weryfikacji ostatniemu z tych komponentów. W momencie jego wywołania możemy być pewni, że poprzednie komponenty zakończyiy już przetwarzanie i przypisały stosowne wartości swoim zmiennym lokalnym. Ostatni komponent przeszedł już proces konwersji — wynik tej konwersji jest przekazywany do metody weryfikującej za pośrednictwem parametru typu ObjecL.
244
Rozdział 6. ■ Konwersja i weryfikacja poprawności danych
JavaServer Faces Opisany model wymaga oczywiście dostępu do pozostałych komponentów. Można ten ceł łatwo osiągnąć, stosując komponent wspomagający, który będzie obejmował wszystkie komponenty bieżącego formularza (patrz listing 6.16). Metodę weryfikującą można związać z tym komponentem wspomagającym w następujący sposób: public class BackingBean { private UIInput daylnput; private UIInput monthlnput; public void validateDate(FacesContext context, UlComponent component. Object value) { int d =((Integer) daylnput.getLocalValue() ) .intValueO: int m =((Integer) monthlnput.getLocalValue() ) .intValueO: int y =((Integer) value) .intValueO; if ( !isValidDate(d, m, y)) { FacesMessage message = throw new VaiidatorException(message);
} Warto zwrócić uwagę na pewną asymetrię w konstrukcji, w której uzyskujemy dostęp do ustawionych wartości. Ostatni komponent nie ustawił wartości lokalnych, ponieważ nie prze szedł jeszcze procesu weryfikacji. Na rysunku 6.12 przedstawiono strukturę katalogów tej aplikacji. Na listingu 6.17 przed stawiono kod odpowiedniej strony JS F . Warto zwrócić uwagę na właściwość converter ostatniego pola tekstowego. Innym ważnym aspektem jest użycie atrybutów binding celem związania komponentów wejściowych interfejsu użytkownika z komponentem wspomaga jącym JavaBean. validator3.war Q META-INF
Rysunek 6.12. Struktura katalogów analizowanej aplikacji
'
Q MANIFEST. MF
WEB-INF f - [ 3 classes | | \
f ■£3 corn f £3 corejsf util
\
j
j
|..Q BackingBean.class
■ ..Q Messages.class
!
'L.Q
messages, properties
| Q faces-config.xmf { Q web.xml
D styles.css Q index.html Q index, jsp D result.jsp
Listing 6.16. Kod komponentu vaiidator3/src/java/com/corejsf'/BackingBean.java 1. package com.corejsf:
8. 9. public class BackingBean { 10 private int day: 11 private int month: 12 private int year, 13. private UIInput daylnput, 14 private UIInput monthlnput: 15 private UIInput yearlnput; 16 17. I I Właściwość day: 18. public int getDayO { return day, } 19 public void setDay(int newValue) { day = newValue; } 20. 21 // Właściwość month: 22 public int getMonthO { return month: } 23 public void setMonth(int newValue) { month = newValue: } 24. 25. // Właściwość year: 26. public int getYearO { return year, } 27 public void setYear(int newValue) { year = newValue: } 28 29. / / Właściwość daylnput: 30. public UIInput getDayInput() { return daylnput: } 31. public void setDaylnputdllnput newValue) { daylnput = newValue: } 32. 33 I I Właściwość monthlnput: 34 public UIInput getMonthInput() { return monthlnput, } 35. public void setMonthInput(UIInput newValue) { monthlnput = newValue; } 36 37 // Właściwość yearlnput: 38 public UIInput getYearInput() { return yearlnput; } 39. public void setYearlnputdlllnput newValue) { yearlnput = newValue, } 40. 41. public void vaiidateDate(FacesContextcontext,UlComponent component, 42 Object value) { 43 int d = ((Integer) daylnput. getLocal Valued) intValueO: 44. int m = ((Integer) monthlnput. getLocal Valued) .intValueO , 45. int y = ((Integer) value) intValueO, 46 47 i f ( !isValidDate(d, m, y)) { 48 FacesMessage message 49. = com.corejsf util Messages,getMessage( 50 "com.corejsf messages", "invalidDate", null): 51 message.setSeverity(FacesMessage.SEVERITY_ERROR), 52. throw new VaiidatorException(message); 53. } 54. } 55 56 private static booleanisValidDatednt d, int m, int y) { 57. i f (d < 1 || m < 1 || m> 12) return false, 58. i f (m == 2) { 59. i f (isLeapYear(y)) return d <= 29: 60. else return d <= 28:
245
246
JavaServer Faces
Rozdział 6. ■ Konwersja i weryfikacja poprawności danych
61
Pole ukryte jest reprezentowane przez pole wejściowe typu hi dden języka HTM L. Mechanizm weryfikujący jest wywoływany w chwili, w której wartość tego pola jest odsyłana na serwer. (Kluczowym warunkiem jest zdefiniowanie jakiejś wartości tego pola — w przeciwnym razie wartość odpowiedniego komponentu nigdy nie zostałaby zaktualizowana). Takie podejście gwarantuje symetrię funkcji weryfikującej, ponieważ wszystkie pozostałe komponenty in terfejsu użytkownika ustawiają swoje wartości lokalne przed przystąpieniem do wykony wania tej funkcji.
else if (rn == 4 || m == 6 II m == 9 || m == 11
62
retu rn
63
d <= 30
el se
64
66 66
retu rn
d <= 31
67
68
private sta tic return
69
boolean i sLeapYear ( int y )
y % 4 == 0 &&
( y % 400
== 0
|
| | y % 100
247
0)
70 71
W ybór niestandardow ego kom ponentu daty obejm ującego trzy pola wejściow e i pojedynczą wartość typu Date nie je s t przypadkowy — prezentowane rozwiązanie znacznie upraszcza weryfikację tego rodzaju danych. Okazuje się jedn ak, że technikę opisaną w tym podrozdziale można z powodzeniem stosować także w przypadku innych danych złożonych z wielu pól wejściowych.
Listing 6.17. Kod strony validator3/web/index.jsp Lag l ID u n =" h t tp //Java sun c om /j sf /c ore ” p r e f i x - ' T Lag l lb ur i =" ht Lp / /J a va sun com /jsf/html" p refix = ” h"
%> %>
c’t u l e x h outputText ve iu e- 'T jn sg s t it l e }"/> pane 1Grid columns-"3">
Alternatywnym rozwiązaniem jest związanie mechanizmu weryfikującego z ukrytym polem wejściowym wchodzącym w skład tego samego formularza (ale zdefmiowanym za pozostałymi polami):
Jak widać, technologia JS F oferuje rozbudowany i rozszerzalny model obsługi zadań konwersji i weryfikacji danych wejściowych. Korzystanie ze standardowych konwerterów i mechani zmów weryfikacji tej technologii wymaga zdefiniowania zaledwie jednego dodatkowego wiersza w kodzie strony JSF. W razie konieczności programista może bez trudu zdefiniować własną logikę bardziej złożonych procesów konwersji i (lub) weryfikacji. W rozdziale 9. dodatkowo omówimy sposób definiowania własnych, niestandardowych znaczników kon wersji i weryfikacji.
248
JavaServer Faces
7
Obsługa zdarzeń Aplikacje internetowe często muszą reagować na takie zdarzenia wywoływane przez użytkow ników jak zaznaczenie opcji menu czy kliknięcie przycisku. Nasza aplikacja może reagować na przykład na wybór kraju, w którym mieszka użytkownik, dostosowaniem ustawień regio nalnych i ponownym załadowaniem bieżącej strony — takie rozwiązanie powinno ułatwić zadanie użytkownikom. Metody odpowiedzialne za obsługę zdarzeń z reguły rejestrujemy dla poszczególnych kom ponentów interfejsu użytkownika — możemy na przykład zarejestrować metodę nasłuchu jącą zmian wartości dla kontrolki menu na stronie JS F :
W powyższym fragmencie kodu, wyrażenie #{forni.countryChanged} odwołuje się do metody countryChanged komponentu nazwanego form. Wspomniana metoda jest wywoływana przez implementację JS F bezpośrednio po tym, jak użytkownik zaznaczy którąś z opcji tak zdefi niowanego menu. Dokładne określenie momentu tego wywołania jest jednym z zagadnień, którymi zajmiemy się w tym rozdziale. Technologia JavaServer Faces obsługuje trzy rodzaje zdarzeń: ■ zdarzenia zmiany wartości; ■ zdarzenia akcji; ■ zdarzenia fazy. Zdarzenia zmiany wartości są generowane przez komponenty wejściowe interfejsu użytkow nika (takich jak h: inputText, h:selectOneRadio i h:selectManyMenu) w reakcji na zmiany wartości tych komponentów. Zdarzenia akcji są generowane przez komponenty poleceń (takie jak h: commandButton i h: commandLi nk) w momencie aktywacji odpowiedniego przycisku lub łącza. Zdarzenia fazy standardowo są generowane przez cykl życia aplikacji JS F . Oznacza to, że jeśli chcemy
250
JavaServer Faces
Rozdział/. ■ Obsługa zdarzeń
obsługiwać tego rodzaju zdarzenia, musimy opanować podstawy tego cyklu życia. Właśnie tym problemem zajmiemy się w pierwszej kolejności.
251
Obiekty nasłuchujące zdarzeń mogą wpływać na cykl życia aplikacji JSF na jeden z trzech sposobów: 1. um ożliwiając norm alną kontynuację cyklu życia; 2. wywołując m etodę FacesContext. renderResponse(), aby pom inąć pozostałe fazy cyklu życia z wyjątkiem fazy wizualizacji odpowiedzi;
Zdarzenia cyklu życia W aplikacjach JavaServer Faces żądania są przetwarzane przez implementację JS F z wyko rzystaniem serwletu kontrolera, który z kolei odpowiada za zarządzanie cyklem życia JSF . Schemat cyklu życia JS F przedstawiono na rysunku 7.1.
Zakończono o d p o w ie dź
Z a kończono o d p o w ie dź
Î
r-------{>
Stosowanie wartości żądania
Przywrócenie
widoku
O
-o
l & S iu . j
Wizualizacja odpowiedzi
Z a kończono o d p o w ie dź
■
-----
o-
Aktualizacja wartości m odelu
W ywołanie aplikacji
B łędy ko n w ersji/w izua liza cja o dp o w ie dzi
—
Przykład użycia metody FacesContext. renderResponse() można znaleźć w podrozdziale „Kom ponenty bezpośrednie” w dalszej części tego rozdziału.
Zdarzenia zmiany wartości
Weryfikacja
W izualizacja o dp o w ie dzi
Z a kończono o dp o w ie dź • o
3 . wywołując m etodę FacesContext. responseComplete(), aby pom inąć wszystkie pozostałe fazy cyklu życia.
;
Błędy w e ryfika cji lu b ko n w ersji/w izua liza cja od p o w ie d zi
Komponenty interfejsu użytkownika aplikacji internetowej często są ze sobą powiązane rozmaitymi zależnościami. Na przykład w aplikacji przedstawionej na rysunku 7.2 tekst etykiety Stan zależy od wartości wybranej w Kraj. Zdarzenia zmiany wartości (generowane przez komponenty wejściowe bezpośrednio po zakończeniu weryfikacji nowych wartości) umożliwiają synchronizację komponentów zależnych.
Rysunek 7.2.
a i łgl i
!f!> Przykład wykorzystania zdarzeń zmiany wartości - Mozilla Rretox
Przykład użycia zdarzeń zmiany wartości
Plik
Ectycja
Widok
-
9
Historia
&
f
Zakładki
i '
Narzędzia
P om oc
££ O
l~'ttp-n|ocalhost:8080/ !
Google___
Proszę wpisać adres Adíes i
Rysunek 7.1. Cykl życia JSF
Pinc
tdycja
Widok
Historia
Zakładki h—p
Cykl życia aplikacji JS F obejmuje następujące fazy: ■ przywrócenie widoku;
Narzędzia
Pomoc
Miasto !
Kraj
; Stany Zjednoczone jr j:
*o c a tH05t: 808 0/ j *
Son g la___j | g | j t
Proszę wpisać adres
Wyślij .adres j
■ stosowanie wartości żądania;
Adres
!
Miasto
i
Prowincja j _ K ra j
■ weryfikacja danych;
! Kanada
IZB
f Wyślij ad;e¿~j
■ aktualizacja wartości modelu;
I Zakończono
■ wywołanie aplikacji; ■ wizualizacja odpowiedzi. Począwszy od fazy stosowania wartości żądania, implementacja JS F może generować zda rzenia i dodawać je do kolejki zdarzeń. Po zakończeniu wszystkich faz następujących po fazie stosowania wartości żądania implementacja JS F rozsyła zakolejkowane zdarzenia do zare jestrowanych obiektów nasłuchujących. Zdarzenia i powiązane z nimi obiekty nasłuchujące będą przedmiotem naszych rozważań w tym rozdziale.
Aplikacja przedstawiona na rysunku 7.2 wiąże metodę nasłuchującą zmian wartości z menu
Kraj i wykorzystuje atrybut onchange do wymuszenia wysyłania całego formularza po każdej zmianie wartości tego menu:
Kiedy użytkownik wybiera kraj ze wspomnianego menu, następuje wywołanie funkcji submi t języka JavaScript, które z kolei powoduje wysłanie całego formularz i w konsekwencji
252
Rozdział 7. ■ Obsługa zdarzeń
JavaServer Faces wywołanie cyklu życia JS F . Po zakończeniu fazy weryfikacji implementacja JS F wywołuje metodę countryChanged komponentu JavaBean nazwanego form. Metoda countryChanged zmienia ustawienia regionalne widoku zgodnie z nową wartością wybraną z menu Kraj : private static String US = "Stany Zjednoczone": public void countryChanged(ValueChangeEvent event) { FacesContext context = FacesContext,getCurrentInstance();
¡ 3 valuechange.war ^ £3 META-INF
Rysunek 7.3. Struktura katalogów aplikacji ilustrującej zdarzenia zmiany wartości
|
| \ S |
if (US.equals((String) event.getNewValue())) context.getViewRoot( ) .setFocale(Focale.US); else context.getViewRoot( ) ,setFocale(Fócale.CANADA),
1
QBD javax.faces.event.ValueChangeEvent
Zwraca komponent wejściowy, który wywołał dane zdarzenie. ■ Object getNewValue() Zwraca nową wartość danego komponentu wejściowego (już po jej konwersji i weryfikacj i). ■ Object getOldVa 1ue() Zwraca poprzednią wartość danego komponentu.
j
*
|.Q
RegisterForm.class
j
;
I.D
messages.properties
|
!
|.Q
messages_en_CAproperties
I
j
'■.Q
messages_en_US.properties
|
1
Q faces-config.xml
|
'■
D web.xml
\
D index htrnl
'
D index jsp________________________________
Listing 7.1. Kod strony valuechange/web/index.jsp 1
2
ta g lib
u r i= " h t t p : / / j a v a .s u n .c o m / js f/ c o re " p r e f i x ^ " f"
%>
3
ta g h b
u n =" h t t p : / / j a v a . s u n . c o n i / j s f / h t i r i l " p r e f i x = - " h "
%>
4 5
< f •v ie w >
6
< 1 1 nk h r e f - " s t y l e s . c s s "
7
< tiU e >
8
■ Ul Component getComponentO
f* t 3 com ( 3 corejsf
j Q st'y4es.css
} Tak jak się dzieje w przypadku wszystkich metod nasłuchujących zmian wartości, także metoda zdefiniowana powyżej otrzymuje na wejściu obiekt zdarzenia zmiany wartości. Na sza metoda wykorzystuje ten obiekt do uzyskania referencji do nowej wartości komponentu interfejsu użytkownika. Klasa Val ueChangeEvent rozszerza klasę FacesFvent (obie należą do pakietu javax. faces .event). Poniżej wymieniono i krótko opisano najczęściej stosowane metody tych klas:
'.....Q MANIFEST.MF
f C3 WEB-INF | f classes
9 10
r e l= " s iy le s h e e t " ty p e - " t e x t /css"/>
v a lu e= "# lm s g s .w in clo w T itlei"/ >
< / L 1 11 e>
11 12
13
< h :outp utT ext
14
15
16
va lu e= "# {m sg s.p a g eT it l e } "
s ty le C la s s = "e m p h a s is "/ >
< h : pane 1G r i d c o l u m n s = " 2 " >
17
< h :outp uiTe xt
18
19
inp u tT e xt
v a lu e = " # { m s g s .s tr e e tA d d r e s s P r o m p t}"/> id= "streetA d duess"
v a lu e = "# {fo rm .s tre e iA d d re s s }"
r e q u ired = "true"/>
20
M javax.faces.event.FacesEvent ■ voidqueueO Umieszcza dane zdarzenie w kolejce celem jego przekazania na końcu bieżącej fazy cyklu życia. ■ Phaseld get Phase 1d()
21
22
■ void setPhaseld(Phaseld) Ustawia identyfikator fazy właściwy dla fazy, w trakcie której przekazano dane zdarzenie.
24 25
v a lu e = " # { m s g s . s t a i e P r o m p t }"/>
v a lu e - "# !fo rm .s ta te }"/ >
26 27
< h : O u t p u t T e x t va 1u e = " # ! m s g s . C O u n t r y P r o m p t } " / >
28 < h:selectO neM enu
v a lu e = "# {fo rm .c o u n try }"
30
o nchange= "subm u()"
31
v a lu eC h a n g e L is ten er= "# {fo nii.countryC hanged}">
32 33
< f- selectIte m s
35
37.
va 1u e = " # ! f o r m . c o u n t r y N a m e s } " / >
< /h:select0 neM en u>
34
36
Strukturę katalogów aplikacji z rysunku 7.2 przedstawiono na rysunku 7.3. Kod źródłowy tej aplikacji przedstawiono na listingach od 7.1 do 7.5.
v a l u e = " # { r i i s g s . c i ty P r o m p t } " / >
23
29
Zwraca identyfikator fazy właściwy dla fazy, w której przekazano dane zdarzenie.
id= "c lty P ro m p t"
v a lu e - "# !fo rm .c ity }"/>
< / h :fo rm >
253
254
Rozdział 7. ■ Obsługa zdarzeń
JavaServer Faces 51. 52. 53. 54. 55. 56 }
38 39 40
Listing 7.2. Kod klasy valuechange/src/java/com/corejsf/RegisterFormjava 1 package com.corejsf, 3 import java.util.ArrayList. 4 import java.util.Collection. 5 import java.util.Locale: 6 import javax.faces.context.FacesContext, 7 import javax.faces.event.ValueChangeEvent: 8 . import javax.faces.model.Select Item, 9 10. public class RegisterForm { 11 private String streetAddress: 12. private String city, 13 private String state: 14. private String country, 15. 16. private static final String US = "Stany Zjednoczone", 17 private static final String CANADA = "Kanada", 18. private static final StringF] COUNTRY_NAMES = { US, CANADA }; 19. private static ArrayFist countryltems = null, 20
Zdarzenia akcji Zdarzenia akcji są wywoływane przez komponenty poleceń (przyciski, łącza itp.) w reakcji na aktywowanie tych komponentów. W podrozdziale „Zdarzenia cyklu życia” wspomniano, że zdarzenia akcji są generowane w trakcie trwania fazy wywołania aplikacji, a więc pod koniec cyklu życia JSF . Metody nasłuchujące zdarzeń akcji z reguły wiążemy z komponentami poleceń interfejsu użytkownika w kodzie stron JSF . Możemy na przykład dodać odwołanie do takiej metody do znacznika definiującego łącze:
Komponenty poleceń wysyłają żądania bezpośrednio po swojej aktywacji, stąd brak koniecz ności dodatkowego wykorzystywania atrybutu onchange do wymuszania wysyłania formu larza (jak w przypadku komponentów wejściowych i zdarzeń zmiany wartości opisanych w podrozdziale „Zdarzenia zmiany wartości” ). Kiedy aktywujemy przycisk lub łącze, formularz, do którego ta kontrolka należy, jest automatycznie wysyłany na serwer, gdzie implementacja JS F generuje kolejno zdarzenia akcji.
Kiedy użytkownik klika twarz prezydenta, nasza aplikacja przenosi go na stronę JS F obej mującą informacje o wskazanym prezydencie. Warto pamiętać, że opisanych zachowań nie da się zaimplementować z wykorzystaniem samej akcji — akcja może powodować nawigację do właściwej strony, ale nie może tej strony zidentyfikować, ponieważ nie dysponuje żadną wiedzą o przycisku reprezentowanym przez obraz, ani o fakcie kliknięcia przycisku myszy. Aplikacja przedstawiona na rysunku 7.4 wykorzystuje przycisk z obrazem zdefiniowany w następujący sposób:
Kiedy użytkownik klika fragment reprezentujący wybranego prezydenta, metoda nasłuchująca, która ma dostęp do współrzędnych kursora w chwili kliknięcia, wybiera właściwego prezy denta (którego biografia zostanie wyświetlona na kolejnej stronie). Ponieważ jednak metoda nasłuchująca nie może decydować o nawigacji, ogranicza się do umieszczenia wyniku wska zującego na właściwego prezydenta w polu obiektu: public class Rushmore { private String outcome; private Rectangle washingtonRect =new Rectangle(70,30,40,40); private Rectangle jeffersonRect =new Rectangle(115,45,40,40); private Rectangle rooseveltRect =new Rectangle(135,65,40,40); private Rectangle lincolnRect =new Rectangle(175,62,40,40);
Metod nasłuchujących akcji w żadnym razie nie należy mylić z samymi akcjami. Najkrócej rzecz ujmując, akcje projektuje się z myślą o logice biznesowej i są uwzględniane w proce sie obsługi nawigacji. Metody nasłuchujące akcji z reguły odpowiadają za funkcjonowanie logiki interfejsu użytkownika i jako takie nie mają wpływu na obsługę nawigacji.
Metody nasłuchujące akcji w niektórych przypadkach współpracują z akcjami (np. w sytuacji, gdy akcje wymagają informacji o interfejsie użytkownika). Na przykład aplikacja przed stawiona na rysunku 7.4 wykorzystuje akcję i metodę nasłuchującą akcji do reagowania na zdarzenia klikania przyciskiem myszy przechodzeniem do określonej strony JSF .
Rysunek 7.4. Aplikacja prezentująca Mount Rushmore
lĘl Naje i metody £“* -idycj» gWiaok
4P1
fiłefo* | N a r z ę d z i a
'I
int x = new Integer((String) requestParams,get(clientld + " .x")).intValueO ; int y = new Integer((String) requestParams,get(clientld + " .y")).intV alu eO ; outcome = n u ll. if (wash!ngtonRect.contains(new Po m tU .y))) outcome = "Washington".
0 "L l§L L JIL j komoę
®®®5'^ii'
257
JŁ 2- SÉ
if (jeffersonRect,contains(new Point(x.y))) outcome = "jefferson".
Aby u?yskuć więcej informacji o prezydencie, kliknijjego rzeźbę.
if UooseveltRect.contains(new Po intu.y ))) outcome = "rooseveli". if (1 mcolnRect. conta ms (new PomL(x.y))) outcome = "lm coln". http^'ocalnostiSOSO/taoDeapane/’naex.race' Zakończono
George Washington był pierwszym prezydentem Stanów Zjednoczonych. Urodził się w roku 1732 w stanie Wirginia. W roku 1775 został mianowany naczelnym wodzem Armii Kontynentalnej. Sześć lat później zmusił do złozema kapitulacji Charlesa Cornwalhsa pod Yorktown. Urząd prezydenta Stanów Zjednoczonych objął 30 kwietnia 17W roku.
Akcja związana z danym przyciskiem wykorzystuje tę wartość do określenia kierunku nawigacji: public String act O { return outcome. 1
258
JavaServer Faces
Rozdział 7. ■ Obsługa zdarzeń
Warto mieć na uwadze, że implementacja JS F zawsze wywołuje metody nasłuchujące akcji przed samymi akcjami.
19
21 Technologia JavaServer Faces wymusza na programistach oddzielanie logiki inter fejsu użytkownika od logiki biznesowej przez eliminowanie możliwości uzyskiwania przez akcje dostępu do zdarzeń i komponentów, które te zdarzenia generują. W przed stawionym przykładzie akcja nie m a dostępu do identyfikatora klienckiego kom ponen tu, który wygenerował dane zdarzenie, czyli informacji niezbędnej do uzyskania współ rzędnych kursora myszy z param etrów żądania. Ponieważ akcja nie dysponuje żadną wiedzą o interfejsie użytkownika, kompletna implementacja wymaga od nas uzupełnienia prezentowanego modelu o m etodę nasłuchującą. Strukturę katalogów aplikacji z rysunku 7.4 przedstawiono na rysunku 7.5. Kod źródłowy tej aplikacji przedstawiono na listingach od 7.6 do 7.13. J 3 ru sh m o re.w ar
Rysunek 7.5.
f P
Struktura katalogów przykładowej aplikacji
META-INF D MANIFEST. MF WEB-INF
j
f- p j
f- p
|
|
i
;
\
\
classes
f -p
com
F f l co rejsf i..D L- Q
\
Rush more, class m e ssag es.p ro p e rtie s
\.....Q faces-config.xm t
j
!..... Q w e b .x m l
r
Q
j
Q
index, ht.ml
j
Q
m o untrushm ore.jp g
styles, css
j -- Q
in d e x .js p
'
Je ffe rso n .jsp
Q
1 0 lincQln.jsp
j~ D roo sevelt.jsp L- D Washington, js p _______________________
action="-#{rushmore navigatef'/>
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.
<1 ink hreK'sty les.css" re I ="sty lesneet" type- 1text/css" />
10 11.
12 .
13 14 16. 1/ 19.
20 22. ${msgs. i ndexLi rikText} 23. 24.
25. 26. 27. 28.
Listing 7.6. Kod strony rushmore/web/index.jsp
Listing 7,8. Kod strony rushmore/web/jefferson.jsp 2. 3. 4. 5. 6. 7. 8. 9. 10 .
Listing 7.13. Plik właściwości rushmore/src/java/com/corejsf/messages.properties_________ 1 instruct!'ons=Aby uzyskać więcej informacji o prezydencie, kliknij jego rzeźbę
2. 3 indexWindowTitle=Akcje i metody nasłuchujące akcji 4. i ndexLi nkText=Powrót... 5. jeffersonlAlindowTitle=Prezydent Jefferson 6 rooseveltWindowTitle=Prezydent Roosevelt 7. lincolnWindowTitle=Prezydent Lincoln 8. washingtonWindowTitle=Prezydent Washington 9. 10. jeffersonPageTitle=Thomas Jefferson 11 rooseveltPageTitle=Theodore Roosevelt 12. lincolnPageTitle=Abraham Lincoln 13 washingtonPageTitle=George Washington 14. 15. jeffersonTabText=Jefferson 16. rooseveltTabText=Roosevelt 17. lincolnTabText=Lincol n 18. washingtonTabText=Washi ngton 19 20. lincolnDiscussion=Prezydent Lincoln przeszedł do historii jako Wielki \ 21. Emancypator, ponieważ w ogromnym stopniu przyczynił się do zniesienia \ 22. niewolnictwa na terenie Stanów Zjednoczonych. Urodzony w 1809 roku wychował \ 23 się w biednej rodzinie w stanie Kentucky. Na urząd prezydenta został wybrany \ 24. w roku 1860. Zginął w zamachu w 1865 roku z rąk Johna Wilkesa Bootha. 25. 26. washingtonDiscussion=George Washington był pierwszym prezydentem Stanów \ 27. Zjednoczonych. Urodził się w roku 1732 w stanie Wirginia. W roku 1775 został \ 28. mianowany naczelnym wodzem Armii Kontynentalnej Sześć lat później \ 29. zmusił do złożenia kapitulacji Charlesa Cornwall isa pod Yorktown Urząd \ 30 prezydenta Stanów Zjednoczonych objął 30 kwietnia 1789 roku. 31. 32. rooseveltDiscussion=Theodore Roosevelt był 26 prezydentem Stanów Zjednoczonych \ 33. Został prezydentem w roku 1901 po zamachu na prezydenta McKinleya. Urząd \ 34. prezydenta objął w wieku 42 lat i przeszedł do historii jako najmłodszy \ 35 prezydent w historii Stanów Zjednoczonych. 36. 37. jeffersonDiscussion=Thomas Jefferson, trzeci prezydent Stanów Zjednoczonych, \ 38. urodził się w roku 1743 w stanie Wirginia. Jefferson był znany z doskonałych \ 39. przemówień. W roku 1785 objął urząd ministra ds. Francji, na którym zastąpił \ 40. Benjamina Franki i na. W roku 1796 Jefferson niechętnie zgodził się kandydować \ 41. na urząd prezydenta, ale przegrał wybory zaledwie trzema głosami. Prezydentem \ 42 Stanów Zjednoczonych został w 1801 roku i pełnił ten urząd przez 8 lat
Znaczniki metod nasłuchujących zdarzeń Do tej pory dodawaliśmy do komponentów interfejsu użytkownika metody nasłuchujące akcji i zmian wartości odpowiednio za pomocą atrybutów actionListener i valueChangeListener. Okazuje się jednak, że metody nasłuchujące można wiązać z komponentami także za pomocą następujących znaczników:
Znaczniki f:actionListener i UvalueChangeListener Znaczniki f : a c ti onLi stener i f: val ueChangeLi stener są odpowiednikami atrybutów actionListener i val ueChangeLi stener. Na przykład menu z listingu 7.1 można by równie dobrze zdefiniować w następujący sposób:
v a l u e = "# {form .co u n try}"
onchang e= "sub m it()"
valueChangeLi stener="#{form.countryChanged}"> < f; s e le c tIte m s
v a l u e = "# {fo rm . countryNam es}"/>
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:
Warto zwrócić uwagę na różne akcje przypisane do poszczególnych łączy: 1ocal eChanger. ^englishAction dla flagi brytyjskiej oraz 1ocal eChanger .poi i shActi on dla flagi polskiej. Okazuje się, że implementacje tych akcji mają postać bardzo prostych metod:
270
JavaServerFaces public class ChangeLocaleBean { public String polishAction!) { FacesContext context = FacesContext.getCurrentlnstance!); context.getViewRoot() .setLocale(Locale.POLISH), return nu ll;
Każda metoda nasłuchująca określa właściwe ustawienia regionalne widoku głównego i zwraca wartość nuli, która sygnalizuje implementacji JS F konieczność ponownego załadowania tej samej strony. To bardzo proste. Wyobraźmy sobie teraz sytuację, w której chcemy zaimplementować obsługę wielu języków — gdybyśmy zdecydowali się na obsługę np. 100 języków, musielibyśmy zaimplementować aż 100 akcji, które różniłyby się tylko stosowanymi ustawieniami regionalnymi. To już nie byłoby takie proste. Aby wyeliminować ten nadmiarowy kod (który wymaga nie tylko napisania, ale też kon serwacji), warto przenieść kod języka z poziomu interfejsu użytkownika na poziom serwe ra. Takie rozwiązanie umożliwiłoby nam zmianę ustawień regionalnych z wykorzystaniem zaledwie jednej akcji lub metody nasłuchującej akcji. Technologia JS F oferuje aż trzy w y godne mechanizmy umożliwiające przekazywanie informacji z interfejsu użytkownika na serwer (wszystkie w formie znaczników JS F ): ■ f:pa ram
Rozdział 7. ■ Obsługa zdarzeń
271
Niezależnie od liczby flag, które zdecydujemy się umieścić na naszej stronie JS P , możemy stosować komponent ChangeLocaleBean w niezmienionej postaci. Oznacza to, że udało nam się wyeliminować nadmiarowość kodu.
Znacznik 1:attribute
■ f :setPropertyActi onLi stener ■ f : attribute W dalszej części tego podrozdziału przeanalizujemy kolejno poszczególne znaczniki pod kątem oferowanych możliwości w zakresie eliminowania nadmiarowego kodu.
Znacznik Uparam Znacznik f:param umożliwia nam dołączanie do komponentów dodatkowych parametrów. Co ciekawe, znaczenie tego znacznika zależy od rodzaju komponentu, dla którego zostanie użyty. Jeśli na przykład dołączymy znacznik f :param do komponentu reprezentowanego przez znacznik h: outputText, implementacja JS F wykorzysta tak zdefiniowany parametr do wypełnienia symboli zastępczych, czyli {0}, {1} itd. Jeśli dołączymy ten sam znacznik do komponentu polecenia (czyli przycisku lub łącza), implementacja JS F przekaże wartość odpowiedniego parametru na serwer w formie parametru żądania. Poniżej przedstawiono możliwy sposób wykorzystania znacznika f : param w przykładowej aplikacji z flagami:
Warto zwrócić uwagę na dwa aspekty tego rozwiązania. Po pierwsze, do ustawienia dodatko wego atrybutu łącza wykorzystano znacznik f :attribute. Sam atrybut nazwano languageCode, a jego wartości (w tym konkretnym przypadku) mogą mieć postać łańcuchów " en" lub " p l".
272
JavaServer Faces Po drugie, tym razem odwołujemy się do metody nasłuchującej akcji (zamiast samej akcji). Zdecydowaliśmy się na takie rozwiązanie, ponieważ metody nasłuchujące akcji otrzymują na wejściu obiekty umożliwiające dostęp do komponentu, który spowodował dane zdarzenie (w tym przypadku będzie to oczywiście jedno z łączy). Naszym celem jest zapewnienie dostępu do wartości atrybutu 1anguageCode. Poniżej przedstawiono odpowiedni fragment kodu wykonywanego po stronie serwera: public class ChangeLocaleBean { public void changeLocale(ActionEvent event) { UlComponent component = event.getComponent(), String 1anguageCode = getLanguageCode(component); FacesContext,getCurrentInstance() ,getViewRoot() . setLocale(new Locale(languageCode));
Tym razem nie wyodrębniamy interesującej nas wartości z parametru żądania — możemy uzyskać tę wartość (w tym przypadku kod wybranego języka) za pośrednictwem atrybutu komponentu. Niezależnie od wybranego modelu przedstawione implementacje komponentu ChangeLocaleBean są kompletne i nie wymagają przebudowy w razie konieczności dodania obsługi nowych języków.
Znacznik i:setPropertyActionUstener Jak wiemy, znaczniki f: pa ram oraz f :a ttrib u te okazują się bardzo wygodne, jeśli chcemy przekazywać informacje interfejsu użytkownika na serwer, jednak wymagają samodzielnego, ręcznego wyodrębniania tych informacji odpowiednio z parametru żądania lub atrybutu komponentu. Znacznik f: setPropertyActi onLi stener (dostępny począwszy od wersji JS F 1.2) eliminuje taką konieczność. Za pośrednictwem tego znacznika możemy wymuszać na implementacji JS F ustawianie wartości określonej właściwości komponentu wspomagającego. Poniżej przedstawiono przykład użycia tego mechanizmu w aplikacji z flagami:
Rozdział 7. ■ Obsługa zdarzeń
273
W powyższym fragmencie kodu JS F sygnalizujemy implementacji JS F konieczność przy pisania właściwości languageCode komponentu wspomagającego local eChanger łańcucha "p l" lub "en". Implementację komponentu 1ocal eChanger przedstawiono poniżej: public class ChangeLocaleBean { private String languageCode; public String changeLocale() { FacesContext context = FacesContext,getCurrentInstance(); context,getViewRoot() .setLocale(new LocaleOanguageCode)); return n u ll;
} public void setLanguageCode(String newValue) { languageCode = newValue;
Na potrzeby przedstawionej implementacji komponentu ChangeLocaleBean zdefiniowano właściwość languageCode (dostępną tylko do odczytu), która jest automatycznie ustawiana przez implementację JSF . W analizowanym przykładzie właśnie użycie znacznika f: setPropertyActi onLi stener wydaje się najlepszą metodą przypisania wartości właściwości languageCode komponentu local eChanger, ponieważ wymaga najprostszej implementacji klasy tego komponentu (ChangeLocaleBean). Okazuje się jednak, że w pewnych przypadkach lepiej sprawdzają się znaczniki f : parani i f a ttrib u te umożliwiające ustawianie odpowiednio parametrów żądania i atrybutów komponentu. Do tej pory koncentrowaliśmy się na technikach dołączania metod obsługujących zdarzenia do egzemplarzy komponentów. Technologia JavaServer Faces dodatkowo umożliwia nam definiowanie mechanizmów obsługi zdarzeń globalnych wywoływanych na różnych etapach cyklu życia JS F . Tego rodzaju zdarzenia omówimy w kolejnym podrozdziale.
Zdarzenia fazy Implementacja JavaServer Faces generuje zdarzenia, nazywane zdarzeniami fazy (ang. phase events), bezpośrednio przed i bezpośrednio po każdej fazie cyklu życia. Tego rodzaju zda rzenia są obsługiwane przez fazowe obiekty nasłuchujące, których — w przeciwieństwie do metod nasłuchujących zmian wartości i akcji — nie wiążemy z poszczególnymi kom ponentami. Fazowe obiekty nasłuchujące wskazuje się w pliku konfiguracyjnym: <1 i fecycle> com.corejsf.PhaseTracker
W powyższym fragmencie kodu wskazano tylko jeden obiekt nasłuchujący, co nie oznacza, że w razie konieczności nie można wskazać dowolnej liczby potrzebnych obiektów. W takim przypadku metody obiektów nasłuchujących będą wywoływane w kolejności zgodnej z po rządkiem w pliku konfiguracyjnym.
274
Rozdział 7. ■ Obsługa zdarzeń
JavaServer Faces KJasy nasłuchujące zdarzeń fazy muszą implementować interfejs PhaseL i stener należący do pakietu javax faces, event. Interfejs PhaseL i stener definiuje trzy metody: ■
Phaseld getPhaselcK )
■
void afierPhase(PhaseEvent)
■
void beforePhase(PhaseEvent)
Rysunek 7.9.
Ź&i Zdarzema fazy
Przykład użycia fazowego obiektu nasłuchującego
Plik
MoziBa h*eiox
Widok
~ i i Wybierz fazę
Historia
Zakładki
Narzędzia
Pomoc
t 3 l i...-. httP://l° -jlt- i * 1 * 1 O . T
P E CT '-,P E „iEv-'
•
lliij
i
„ p p L , _ B E 'JU F < T T _ - a L i i E':.
P R O C E S S ■■-LlE'ATinriS ■j B D ^ t e . m O P E L .» « l u E E It J- Ol E P E ' l E 'E P .P E S P ' 'OSE ^ ■ P h «S E ^ i vW siij lomiuiatz j
Metoda get Phase 1d sygnalizuje implementacji JS F konieczność przekazania informacji 0 zdarzeniu fazy metodzie nasłuchującej — metoda get Phase i d może zwrócić na przykład wartość Phase Id. APPly_REQUEST_VALUES. W takim przypadku metody befonePhase oraz aftenPhase zostałyby wywołane tylko raz w całym cyklu życia (bezpośrednio przed i po fazie stosowania wartości żądania). Ta sama metoda może też zwrócić wartość Phase Id ^ANY PhASE, która w praktyce reprezentuje wszystkie fazy. W takim przypadku każda z metod beforePhase oraz afterPhase zostałaby wywołana aż sześć razy w jednym cyklu życia: przed 1po każdej fazie tego cyklu.
Fazowe obiekty nasłuchujące są szczególnie przydatne w procesie diagnozowania opro gramowania i w przypadku wysoce wyspecjalizowanych rozwiązań. Jeśli na przykład uży jemy komponentów JS F w aplikacji internetowej opracowanej na bazie innego frameworku, np. Struts, możemy dążyć do aktualizacji ustawień regionalnych tego frameworku po fazie stosowania wartości żądania (czyli wtedy, gdy implementacja JS F wewnętrznie okre śla swoje ustawienia regionalne). Fazowe obiekty nasłuchujące także w przypadku aplikacji wykorzystujących technikę A JA X służą do poprawy funkcjonalności interfejsu użytkownika (patrz rozdział II.). Aplikacja przedstawiona na rysunku 7.9 ilustruje możliwe zastosowanie fazowych obiektów nasłuchujących. Nasza przykładowa aplikacja wykorzystuje pojedynczy obiekt nasłuchujący do rejestrowania komunikatów:
Edycja
275
Edycja
Widok
Histeria
T ,‘ ":r S -z Zakładki
Narzędzia
S W vbi^rz fazę
phLse
Pomoc
B
tj
ilii EJ
R E E T ' .
«U,.Ph-SE W ysit; tormuiąrz ¡
mmmmi pub 11C C lass FormBear { //
public class PhaseTracker implements PhaseListener {
METODA NASŁUCHUJĄCA ZMIAN WARTOŚCI: phaseChange
public void phas eCra rg e (v a lueCrargeEvert e) ]
/ / uzyskujemy referencję do bieżącego cyklu życia. private static final Logger logger = Logger.getLogger!"com.corejsf.phases"); PhaseListener[] listeners = 1ifecycle.getPhaseListeners!), for (int i=0; i < listeners length; i++) { PhaseListener listener = listeners[i]; i f (listener instanceof com.corejsf.PhaseTracker) ( (com.corejsf.PhaseTracker) 1i stener).setPhase((String) e.getNewValue());
public void beforePhase(PhaseEvent e) { logger.info("PRZED " + e.getPhaseldC));
}
public void afterPhase(PhaseEvent e) { logger.infoC'PO " + e.getPhaseldO);
}
} } Identyfikator fazy jest wybierany (na potrzeby tego obiektu) z listy zdefiniowanej w kodzie strony JS F w następujący sposób:
Kiedy użytkownik zaznacza fazę i aktywuje przycisk akceptacji formularza, metoda nasłu chująca zmian wartości ustawia właściwy identyfikator fazy (na potrzeby fazowego obiektu nasłuchującego):
} } Na pierwszym ze zrzutów widocznych na rysunku 7.9 przedstawiono początkową stronę analizowanej aplikacji — łatwo zauważyć, że na tym etapie nie zaznaczono jeszcze żadne go elementu listy. Ponieważ domyślną fazą obiektu nasłuchującego jest PhaseId.ANY_PHASE, wspomniany obiekt będzie wywoływany bezpośrednio przed i po każdej fazie (aż do momentu zaznaczenia któregoś z elementów listy i wysłania formularza na serwer). Poniżej przedsta wiono dane wyjściowe zapisane na początku działania aplikacji przez analizowany obiekt nasłuchujący w pliku dziennika kontenera serwletu:
276
Rozdział 7. ■ Obshiga zdarzeń
JavaServer Faces INFO: INFO INFO: INFO:
PRZED RESTORE_VIEW 1 PO RESTOREJIEW 1 PRZED RENDER_RESPONSE 6 PO RENDER_RESPONSE 6
Dlaczego obiekt nasłuchujący nie został poinformowany o fazach od drugiej (stosowanie wartości żądania) do piątej (wywołanie aplikacji)? W momencie uruchamiania aplikacji nie istnieje widok, który można by przywrócić, ponieważ nie załadowano jeszcze żadnej strony JSF . Skoro nie istnieje drzewo komponentów i — tym samym — weryfikacji danych wej ściowych, aktualizacja wartości modelu ani wywoływanie akcji nie ma najmniejszego sensu; najlepszym rozwiązaniem jest przejście cyklu życia JS F bezpośrednio do fazy wizualizacji odpowiedzi. Taka sytuacja ma miejsce tylko na początku działania aplikacji i zmienia się w chwili jej ponownego załadowania przez kontener serwletów. Oznacza to, że jeśli użytkow nik aktywuje przycisk wysyłający formularz bez zaznaczania któregokolwiek z elementów listy, w pliku dziennika zostaną umieszczone następujące dane wyjściowe: INFO: INFO: INFO: INFO: INFO. INFO: INFOINFO: INFO: INFO: INFO: INFO:
PRZED RESTORE_VIEW 1 PO RESTOREJ/IEW 1 PRZED APPLY_REQUEST_VALUES 2 PO APPLY_REQUEST_VALUES 2 PRZED PROCESS_VALI DATIONS 3 PO PROCESSJ/ALIDATIONS 3 PRZED UPDATE_MODEL_VALUES 4 PO UPDATE_MODEL_VALUES 4 PRZED I NVOKE_APPLICATION 5 PO INVOKE_APPLICATION 5 PRZED RENDER_RESPONSE 6 PO RENDER_RESPONSE 6
Jak widać, tym razem obiekt nasłuchujący jest informowany o wszystkich fazach, ponieważ jako fazę domyślną ustawiono Phaseld. ANY_PFIASE. W kolejnym kroku zaznaczamy element listy nazwany APPLY REQUEST VALUES (repre zentujący fazę stosowania wartości żądania) i aktywujemy przycisk akceptacji formularza (patrz środkowy zrzut ekranu na rysunku 7.9). Poniżej przedstawiono dane wynikowe zapisane w pliku dziennika przez obiekt nasłuchujący: INFO INFO 1NF0 [NFO [NFO
Ponieważ faza stosowania wartości żądania już się zakończyła, nasz obiekt nasłuchujący nie został poinformowany o pozostałych fazach danego cyklu życia JSF. Jeśli ponownie klikniemy przycisk akceptacji formularza, dane wynikowe będą już w pełni zgodne z naszymi ocze kiwaniami, ponieważ identyfikator fazy w ramach obiektu nasłuchującego od początku nowego cyklu życia będzie wskazywał na fazę stosowania wartości żądania: INFO- PRZED APPLY_REQUEST_VALUES 2 INFO- PO APPLY_REQUEST_VALUES 2
I wreszcie zaznaczamy element INV0KE_APPLICAT10N i wysyłam y formularz na serwer. W wyniku tego kroku otrzymamy następujące dane wyjściowe: INFO: INFO: INFO: INFO
PRZED APPLY_REQUEST_VALUES 2 PO APPLY_REQUEST_VALUES 2 PRZED INVOKE_APPLICATION 5 PO INVOKE_APPLICATION 5
Czy na tym etapie przedstawione dane wynikowe mają jakiś sens? Na początku nowego cyklu życia identyfikator fazy obiektu nasłuchującego wskazuje na fazę stosowania wartości żądania, zatem właśnie o niej jest informowany. W dalszej części tego cyklu życia (po fazie wery fikacji) metoda nasłuchująca zmian wartości przypisuje temu identyfikatorowi wartość INVOKE_APPLICATION. Chwilę później ten sam obiekt nasłuchujący jest informowany o fazie wywołania aplikacji. Jeśli ponownie klikniem y przycisk akceptacji formularza, w pliku dziennika zostaną umieszczone następujące dane wyjściowe: INFO. PRZED INVOKE_APPLICATION 5 INFO PO INVOKE_APPLICATION 5
Strukturę katalogów tej prostej aplikacji przedstawiono na rysunku 7.10. Kod źródłowy aplikacji z rysunku 7.9 przedstawiono na listingach od 7.14 do 7.19.
Rysunek 7.10. Struktura katalogów analizowanej aplikacji przykładowej
3 phase-track.er.war
Y
META-INF ! L Q MANIFEST.MF f CaWEB-INF f [ 3 classes ? (¡3 com t k3 corejsf
PRZED RESTORE_VJEW 1 PO RESTORE_VIEW 1 PRZED APPLY_REQUEST_VALuES 2 PO APPLY_REQUEST_VALllES 2 PRZED PROCESSJALI DATIONS 3
Q Form B ean class Q P h as eT racke r class Q m essag es p ro p e rtie s Q fa c e s -c o n tig xml
¡2 w e b xm i D
Część Czytelników zapewne oczekiwała informacji tylko o fazie stosowania wartości żądania. Dlaczego nasz obiekt nasłuchujący został poinformowany o zdarzeniach przed i po fazie przywracania widoku oraz przed fazą weryfikacji? Po pierwsze, w momencie wysyłania for mularza w obiekcie nasłuchującym nadal obowiązuje faza ANY_PFiASE. Oznacza to, że jeszcze na początku danego cyklu życia nasz obiekt nasłuchujący byl zainteresowany wszystkimi fazami (i byl o nich informowany). Po drugie, musimy pamiętać, że identyfikator fazy w ramach naszego obiektu nasłuchują cego jest ustawiany przez metodę nasłuchującą zmian wartości, a tego rodzaju metody są wywoływane po fazie weryfikacji (patrz rysunek 7.1). Na końcu fazy weryfikacji metoda nasłuchująca zmian wartości przypisuje odpowiedniej składowej fazowego obiektu nasłu chującego wartość APPLY_REQUEST_VALUES.
277
styles css
Q in d e x html Q index jsp_______________________
Przedstawiony mechanizm śledzenia faz można wykorzystywać we własnych aplika cjach celem sprawdzania, kiedy poszczególne zdarzenia są generowane — wszystkie generowane przez nas kom unikaty dziennika będą występowały w łącznej sekwencji kom unikatów generowanych przez opisany m echanizm . Aby stosow anie tego m echa nizmu było m ożliw e, koniecznie należy um ieścić odpowiedni plik klasy w pliku WAR i w łaściw ą deklarację w pliku konfiguracyjnym faces~config.xml (patrz listing 7 .1 7 ). Omówienie bardziej rozbudowanego i zaawansowanego mechanizmu śledzenia JSF można znaleźć w punkcie „Jak diagnozow ać a p lik ac ję, która u tkn ę ła na ja k ie jś stronie?" w rozdziale 1 3 .
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;
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
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;
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:
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:
Pfzvttad pareiu pocsziesoneqo na zagadki -MözWafsrefoac '
Przykład panelu podzielonego na zakładki
Pt*
Edycja
Widok
•cy * i,iÿP i>
Historii i »
jj j| !
Zakłsaw
Narzędas
’
- 1 @ L U E JL S J
Pomoc
http://localhost:8080/tabbedpane/index.faces
.;■> ] f kjfcj-.
Ooorçic
p tł
TŁeodore Rooseyeltl jAbrahamLincolnI IGeorge Washingtoni ir2ect prczydenł Stanów Zjednoczonych urodził się w mku 17&3 w stanie Wirginia Jefferson byt znany z doskonałych przemówień Wraku ł 786 nbjał urząd ministra d&
Thomas Jeffersonj
■AbrahamUrnolrT ¡George Washington’
Theodore Roosevelt byt 26 prezydentem Sianów Zjednoczonych. Zostałprezyaentem w mku ¡901 po zamachu na prezydenta McKinley a. Urząd prezydenta objął w wieku ¿7 lat i orzeszedł kistom jako n aj*iłodszv w histom Stanów Zjednoczonych.
Prezydent Lincoln przeszedł do hislom jako Wielki Bmancypator , ponieważ w ogromnym stopniu przyczynił się Jo zniesienia niewolnictwa na tereme Stanów Zjednoczonych Urodzony vu ¡809 roku wychował się w biednej rodzime w stanie Kentucky. Na nnądprezydenta został wybrany w r0ku 1860.
IThgraas JeftersoBj
Kiedy użytkownik klika zakładkę, następuje wywołanie odpowiedniej metody nasłuchującej akcji (związanej z danym łączem polecenia), która modyfikuje dane składowane w kompo nencie wspomagającym. Ponieważ dla zaznaczonej zakładki stosujemy inny styl C SS niż dla pozostałych zakładek, wartość atrybutu sty 1eClass każdego znacznika h:commandl_ ink jest odczytywana z komponentu wspomagającego za pośrednictwem wyrażenia reprezentującego referencję do wartości. Jak widać na pierwszym zrzucie z rysunku 7.11, wykorzystaliśmy atrybut t it le do przypi sania poszczególnym zakładkom łańcuchów podpowiedzi. Innym ważnym elementem funkcjonalności jest mechanizm umożliwiający przełączanie zakładek za pomocą klawiatury (bez konieczności klikania odpowiednich nagłówków). Zaimplementowaliśmy ten mecha nizm, przypisując odpowiednie wartości atrybutom tab index poszczególnych znaczników h:commandLink. Treść właściwa dla poszczególnych zakładek jest dołączona statycznie za pomocą dyrektywy include znanej z technologii JSP. W tym konkretnym przypadku treść ma postać prostego obrazu i niedługiego tekstu, co nie oznacza, że nie można by tego mechanizmu rozbudować o bardziej zaawansowany zbiór komponentów. Warto zwrócić uwagę na to, że mimo dołą czania wszystkich stron JS F reprezentujących właściwą treść (w tym przypadku material o czterech prezydentach) wizualizacji podlega wyłącznie treść właściwa dla wybranej zakładki. Takie rozwiązanie jest możliwe dzięki zastosowaniu atrybutu rendered — w przypadku strony jefferson.jsp odpowiednia konstrukcja ma następującą postać:
Oeorge Washington był pierwszym prezydentem Stanów Zjednoczonych. U''odziiste w roku i? 32 w stanie Wirgsma. W roku ¡775 został mianowany naczelnym wodzem Armn Kontynentalnej. Szesc łat później zmusił do zbzenm kapitulacji Charlesa Carnwalłtsapad Yorklown. Urząd prezydenta Stanów Zjednoczonych objął 30 kwietnia ¿789 roku.
s ly 1eClass ="#{ip .jeffersonStyle}" act i oriL i s tener-"#{ Lp. jef f ersonAct i on} "> <%-- Panel główny --%>
include file="jefferson.jsp" %> <%$ include file="roosevelt.jsp" %> <%@ include file="lincoln.jsp" %> include file="washington.jsp" %>
Panel podzielony na zakładki zaimplementowano z wykorzystaniem znacznika h: panel Gri d. Ponieważ zrezygnowaliśmy z przypisywania jakiejkolwiek wartości atrybutowi col umns, nasz panel będzie się składał tylko z jednej kolumny. Nagłówek panelu (zdefiniowany w formie znacznika f: facet) zawiera zakładki zaimplementowane za pomocą kolejnego znacznika h: panel Gri d obejmującego po jednym znaczniku h:commandLink dla każdej zakładki. Jedyny wiersz naszego panelu zawiera opis właściwy dla wybranej zakładki.
283
Na rysunku 7.12 przedstawiono strukturę katalogów opisywanej aplikacji. Kod źródłowy tego przykładu przedstawiono na listingach od 7.20 do 7.28.
Rysunek 7.12. Struktura katalogów naszej przykładowej aplikacji
¡3 tabbedpane.war y-|«I META-INF | L D MANIFEST.MF f-ü3 WEB-INF | classes | | fcom j | f ■(¡3 corejsf | | f Ew3util | ! I L-D Messages.class !
I M Implementacja referencyjna JSF obejmuje katalog sampłes> w którym można znaleźć ■ ¿ J całkiem sporo aplikacji przykładowych. Jedna z nich (umieszczona w pliku Jsf-components.war) ilustruje działanie kom ponentu panelu podzielonego na zakładki. M im o że dołączane kom ponenty przykładowe m ają na celu bardziej prezentowanie ogólnych koncepcji i założeń niż im plem entow anie gotowych rozwiązań, warto je wykorzystywać na wczesnych etapach realizacji własnych projektów (w tym prac nad własnymi kom ponentam i niestandardowym i).
Listing 7.20. Kod strony tabbedpane/web/index.jsp 1 2 3 4. 5. 6. 7. 8. 9. 10 . 11.
sty leClass="/f{ tp.wasrnngtonStyle}" act i onus tener=“#{ tp.washmgtonAct ion}">
49
50 51 52 53 54 55 <%■ ■ Zawartość panelu z zakładkami ■■%> 56 57 <%G include fi le="wasmngton.jsp" °x> 58 <%G include i i le="rooseveU. jsp" %> 59 include f i ie=" 1incoln. jsp" %> 60 <70@ include f 11e="jefferson.jsp" 70> 61. 62 63. 64. 65.
Listing 7.21. Kod strony tabbedpane/web/jefferson Jsp 1. 3.
4. 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.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 .
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.
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
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:
tytuł i poziomą linię —%>
296
Rozdział 8. ■ Podwidoki i pakiet Apache Tiles
JavaServer Faces
297
¡3 book-viewer.war fm eta-inf
Rysunek 8.4. Struktura katalogów aplikacji przeglądarki książek
I
L Q MANIFEST.MF
f C3 WEB-INF j
L ( ¡ 3 c la s s e s
M Î Ö com Ï I f ö corejsf ; ! i Q Book, class
obejmująca menu z łączami do rozdziałów —%>
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
Q cnapter? htmi
Q chapters htmi Q chapters htmi
Przeglądarkę książek w tej wersji zaimplementowano z wykorzystaniem siatki panelu obejmu jącej dwie kolumny. Obszar nagłówka jest wypełniany obrazem, tekstem i poziomą linią języka HTM L. Nasza siatka zawiera (oprócz nagłówka) tylko jeden wiersz, który w pierwszej kolumnie i drugiej kolumnie obejmuje odpowiednio menu i treść bieżącego rozdziału. Menu składa się z łączy wskazujących na kolejne rozdziały. Metoda Book .getChapterKeysU domyślnie zwraca listę łańcuchów w następującej formie: chapterl chapter2 chdpierN
ChapterN reprezentuje ostatni rozdział danej książki. W pakiecie komunikatów zdefiniowano dla tych kluczy następujące wartości: chapterl=Rozdział 1. chapter2=Rozdział 2.
Do utworzenia odpowiednich łączy wykorzystano znacznik h: dataTabl e, który iteracyjnie przeszukuje klucze właściwe dla poszczególnych rozdziałów książki. Dla każdego rozdziału tworzymy łącze, którego tekst jest generowany na podstawie wartości odpowiedniego klucza — takie rozwiązanie jest możliwe dzięki użyciu wyrażenia #{msgs[chapterKey]}. Oznacza to, że dla książki podzielonej na 12 rozdziałów otrzymamy łącza od Rozdział 1. do Rozdział 12. Prawą kolumnę zarezerwowano dla treści wybranego rozdziału. Samą treść dołączamy za pomocą znacznika c : i mport biblioteki JST L . Strukturę katalogów aplikacji przeglądarki książek przedstawiono na rysunku 8.4. Kod wersji opracowanej na bazie monolitycznej strony JS F przedstawiono na listingach od 8.1 do 8.5.
Q index html D Cheshire jpg Q book jsp_______________________
W arto zwrócić uwagę na znacznik f : param użyty w ramach znacznika h:commandlink. Framework JSF przekształca tak zdefiniowany param etr w param etr żądania (w tym przypadku nazwany chapter) w odpowiedzi na aktywację danego łącza. W czasie ponow nego ładow ania strony w artość tego param etru wskazuje na właściwy rozdział: 1 X." .
Dołączanie treści w ramach aplikacji przeglądarki książek
[ D chapter6.html F D chapter7.html [- D chapter8.html F
D
chapter9.html
j D index.html r D cheshire.jpg j- D book.jsp
Aby dołączyć treść w ramach przeglądarki książek, należy podzielić naszą monolityczną stronę JS F na cztery odrębne pliki: oryginalną stronę JS F oraz strony /header.jsp, /menu,jsp i /contentjsp. Do naszej oryginalnej strony JS F będziemy dołączali nagłówek, menu i wła ściwą treść:
303
p Q bookContent.jsp F Q bookH.e-ader.jsp ! - D bookMenu.jsp__________
Prezentacja pakietu Apache Tiles W iem y już, jak dołączać treść do stron aplikacji internetowych i jak stosowanie tej techniki wpływa na poprawę elastyczności budowanych rozwiązań — jeśli właściwa treść nie jest wymieszana z pozostałym kodem, tylko dołączana, jej ponowne wykorzystywanie jest dużo łatwiejsze. Skoro możemy teraz tworzyć interfejsy użytkownika z wymiennymi elementami, wydaje się, że osiągnęliśmy właściwy poziom elastyczności. Warto jednak zwrócić uwagę na jeszcze jedno, bardzo kuszące rozwiązanie. Dołączanie treści to nie wszystko — można jeszcze wykorzystać pakiet Apache Tiles do dołączania rozmieszczenia (układu strony). W przypadku aplikacji z rysunku 8.2 dołączanie rozmieszczenia umożliwiłoby wielokrotne wykorzystywanie kodu układu (czyli znacznika h: panel Gri d i jego zawartości przedstawionych na listingu 8.6). Kod układu przedstawiony na listingu 8.6 może być wykorzystywany wyłącznie przez stronę JS F przedstawioną na ry sunku 8.2. Oznacza to, że gdybyśmy chcieli stosować identyczny układ na innych stronach JS F , w każdej z nich musielibyśmy ten kod powielić. Za pomocą pakietu Tiles możemy zdefiniować pojedynczy układ, by następnie wielokrotnie go wykorzystywać w ramach wielu tzw. kafelków (ang. tiles). Takie rozwiązanie umożliwiłoby nam implementację kodu rozmieszczenia tylko raz i wielokrotne stosowanie tej konstrukcji w ramach wielu stron aplikacji.
Listing 8.9. Kod strony book-viewer-include/web/bookContent.jsp_________________ 1. <7@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
2. 3.
Okazuje się, że wielokrotne wykorzystywanie rozmieszczeń jest tylko jednym z zastosowań pakietu Apache Tiles. Część z nich wymieniono poniżej: ■ zagnieżdżanie kafelków; ■ rozszerzanie kafelków; ■ ograniczanie dostępu do kafelków dla użytkowników występujących w określonych rolach; ■ dołączanie do kafelków kontrolerów (obiektów Javy) wywoływanych bezpośrednio przed wyświetlaniem tych kafelków. Te najważniejsze elementy funkcjonalności pakietu Tiles mają na celu osiąganie możliwie wysokiej elastyczności w procesie wytwarzania interfejsów użytkownika aplikacji interne towych.
Instalacja pakietu Tiles Stosowanie pakietu Tiles wymaga użycia autonomicznego pliku JA R . Odpowiedni plik dołączono do kodu źródłowego opracowanego z myślą o tej książce. Kiedy już będziemy dysponowali plikiem JA R pakietu Tiles, instalacja tego pakietu w ramach naszej aplikacji będzie polegała na wykonaniu następujących kroków: 1.
Skopiowanie pliku JA R pakietu Tiles do katalogu WEB-INF/lib tej aplikacji.
306
JavaServer Faces 2. Dodanie serwletu Tiles do deskryptora wdrożenia [web.xml). Stosując element 1oad- on -startup, możemy zagwarantować ładowanie serwletu Tiles w momencie
uruchamiania naszej aplikacji internetowej. W naszym przypadku wspomniany deskryptor wdrożenia powinien mieć następującą postać: Faces Servietjavax.faces.webapp.FacesServletlTiles Servletorg.apache.t i l es.servlets.TilesServlet2
Stosowanie pakietu Tiles w ramach aplikacji przeglądarki książek Stosowanie pakietu Apache Tiles łącznie z technologią JS F jest procesem trzy etapowym: 1. wykorzystanie znacznika t i 1es: i nsert do wstawienia definicji kafelka w ramach stron JS F ;
2. zdefiniowanie kafelka w pliku konfiguracyjnym pakietu Tiles; 3. implementacja układu kafelka. W przypadku aplikacji przeglądarki książek powinniśmy w pierwszej kolejności umieścić kafelek nazwany book w kodzie strony book.jsp : <%@ taglib uri="http://jakarta.apache.org/tiles" prefix="tiles" %>
Kafelek book należy zdefiniować w pliku konfiguracyjnym /WEB-INF/tiles.xml: d efin ition name="book" path="/headerMenuContentLayout.jsp">
Rozdział 8. ■ Podwidoki i pakiet Apache Tiles
307
Powyższy fragment kodu XM L-a definiuje pojedynczy kafelek. Układ tego kafelka definiu jem y za pośrednictwem atrybutu path elementu def i ni ti on. Tak zdefiniowane rozmiesz czenie jest uzależnione od atrybutów kafelka (określone z wykorzystaniem elementów put). Układ analizowanej aplikacji można by opisać następująco: < % — kod strony /headerMenuContentLayout.jsp
Znacznik t i 1es: i nsert dynamicznie dołącza odpowiednią treść. Dołączana treść jest repre zentowana przez wartość atrybutu a ttri bute użytego w ramach znacznika t i 1es: i nsert. Na przykład w powyższym fragmencie kodu jednemu z atrybutów przypisano wartość header. Ponieważ wspomniana wartość reprezentuje stronę /bookHeader.jsp, odpowiedni znacznik ti 1es: i nsert dynamicznie dołączy właśnie ten plik. Łatwo zauważyć, że we wszystkich znacznikach t i l es: i nsert dodatkowo przypisaliśmy atrybutowi flush wartość false. Taki krok jest konieczny w przypadku większości współ czesnych kontenerów serwletów, które nie oferują możliwości opróżniania buforów w ramach znaczników niestandardowych. Jeśli nasz kontener generuje wyjątek wskazujący na brak możliwości opróżniania bufora z poziomu znacznika niestandardowego, możemy być pewni, że faktyczną przyczyną błędu jest omyłkowe pominięcie tego atrybutu (którego domyślną wartościąjest false). Co właściwie zyskujemy, stosując pakiet Apache Tiles w naszej przykładowej aplikacji? Oddzielenie układu (rozmieszczenia) od właściwej treści, umożliwia nam jego wielokrotne wykorzystywanie w imiych kafelkach (zamiast powielać kod układu we wszystkich stronach JS F ). Moglibyśmy na przykład wykorzystać układ aplikacji przeglądarki książek (zaim plementowany w kodzie strony /headerMenuContentLayout.jsp) w pozostałych stronach tej samej aplikacji.
308
Rozdział 8. ■ Podwidoki i pakiet Apache Tiles
JavaServer Faces
Rozszerzanie kafelków
Parametryzacja kafelków Układ przedstawiony w poprzednim punkcie ma jedną zasadniczą wadę — obejmuje trwale zakodowane klasy C SS nazwane gridClass, headerClass, menuClass i contentClass. Ozna cza to, że wszystkie strony zbudowane z wykorzystaniem rozmieszczenia nagłówek, menu, treść będą się cechowały identycznym wyglądem i sposobem obsługi. W tej sytuacji bardzo pożądana byłaby możliwość parametryzacji nazw klas CSS. Pozostałe kafelki wykorzystujące układ nagłówek, menu, treść mogłyby wówczas definiować własny wygląd i sposób obsługi. W tym punkcie dokonamy analizy sposobu realizacji tego celu. W pierwszej kolejności dodamy trzy atrybuty do kafelka book:
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»"...
1 ■Q messages.properties
j ?■ E S lib I j • Q tiles-core-SNAPSHOT.jar ; j- Q faces-config, xml j ¡- Q tiles.xml ;
Q web.xml
f ■D sîytes.css i- D chapterl.html j- D chapteri0.html j-- Q chapterll.htmf
F D chapterl2.html D chapter2.html j- Q chapter3.html |-- Q chapter4.html \ Q chapter5.html F
D chapters, html
r D chapter7.html F
D chapter8.html
F Q chapters*.htmf
Listing 8.11. Kod strony book-vlewer-tiles/web/headerMenuContentLayout.jsp 1. 2 3. 4. 5.
|~ D book.jsp F D bookContent.jsp F Q bookHeader.jsp F
D bookMenu.jsp Q headerMenuContentLayout.jsp
Na listingach od 8.10 do 8.12 przedstawiono plik definicji kafelków, plik opisujący układ strony oraz kod strony JS F prezentującej książkę Alicja w Krainie Czarów. Zrezygnowano z ponownego przedstawiania listingów pozostałych plików tej aplikacji, ponieważ niczym się nie różnią od listingów zaprezentowanych we wcześniejszej części tego rozdziału.
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
Warto zwrócić uwagę na wartość przypisaną atrybutowi book — tym razem nie jest to strona JSP , tylko kafelek. Stosowanie nazw kafelków (zamiast stron JS P ) umożliwia nam zagnież dżanie kafelków, w tym przypadku kafelka book, w ramach biblioteki. Aficja w Krainie Czarów f/Ęj SMoteka Gutenfcercja Plik
Edycja
wtem
• si!$& -
Historia i?',f | j) | !
¿akraaki
Kontroler kafelków
tera
Irttp://localhost|9090/libiaiy/libraiyi»ces
iWybierz książkę j
W naszej aplikacji przeglądarki książek wykorzystywaliśmy tylko jeden komponent zarzą dzany, nazwany book (więcej informacji o tym komponencie można znaleźć w podrozdziale „Przeglądarka książek"). Z drugiej strony, nasza biblioteka musi oferować obsługę więcej niż jednej książki.
Bvii>v ,vam trudn' zrozumie' przvgn L Piotrusia Pana
rn- w r laeL i
Leśny Park ten jest olbrzymi i lezy w pobliżu bardzo wielkiego miasta, w którym mieszka król Prawie codziennie chodzimy z Daniem do parku i Danio jest wprost oszołomiony mnóstwem wrażeń, jakich tam doznaje Nigdy jeszcze żadne dziecko me zwiedziło całego parku za jednym razem, bo malcy w wieku Dama muszą sypiać od dwunastej do pierwszej w południe i dlatego trzeba spieszyć z powrotem do domu Gdyby jednak wasze mamy me przestrzegały tak koniecznie tego spania od dwunastej do pierwszej w południe, to może moglibyście zwiedzić cały Park Leśny w jednym i tym samym dniu Przed parkiem ciągnie się nieskończenie długi sznut dorożek, nad którymi wasze piastunki mają taką władzę, ze skoro palec do góry podniosą, dorożka staje, a wtedy dzieci przechodzą swobodnie na drugą stronę ulicy i zatrzymują się przed wielką, zelazną bramą Bram w parku jest kilka, ale zwykle wchodzi się tą pierwszą U wejścia można zamienić parę słów z „parną od baloników” Pani ta z takim natęzemem całą swą przysadzistą postacią przyciska stołek do bruku chodnika, a plecy do żelaznego ogrodzenia parku, ze twarz jej jest czerwona jak burak Ostrożność ta jest bardzo wskazana, bo gdyby choć na chwilkę puściła się sztachet i stołka,
Przedstawiona na rysunku 8.7 aplikacja biblioteki zawiera między innymi menu w górnej części strony. Za pośrednictwem tego menu użytkownik może wybrać interesującą go książkę: Alicję w Krainie Czarów lub Piotrusia Pana. Pozostałe elementy aplikacji pracują dokładnie tak samo jak w przeglądarce książek, którą omówiliśmy we wcześniejszych podrozdziałach tego rozdziału. W aplikacji biblioteki wykorzystano dwa szczególnie interesujące elementy funkcjonalności pakietu Apache Tiles: zagnieżdżanie kafelków oraz kontrolery kafelków.
W tym punkcie pokażemy, jak opracować rozwiązanie umożliwiające obsługę wielu ksią żek bez konieczności modyfikowania istniejącej przeglądarki książek. Nasza przeglądarka nadał będzie operować na komponencie book, ale nie będzie to już komponent zarządzany. Komponent book będzie teraz reprezentował książkę, która została jako ostatnia wybrana z menu rozwijanego w górnej części strony biblioteki. Opisywane rozwiązanie jest możliwe dzięki użyciu kontrolera kafelków. Pakiet Tiles oferuje możliwość dołączania do kafelków obiektów Javy nazywanych kontrolerami kafelków (ang. tile controllers). Klasy tych obiektów muszą implementować interfejs org .apache. struts. ^ t i 1e s. Control 1er definiujący tylko jedną metodę: perform. Pakiet Tiles wywołuje tę metodę bezpośrednio przed załadowanie kontrolera właściwego dla danego kafelka. Kon trolery kafelków mają dostęp do kontekstu kafelków, zatem mogą uzyskiwać dostęp do ich atrybutów, a także tworzyć nowe atrybuty. Zdecydowaliśmy się dołączyć kontroler do kafelka 1ibrary. Nasz kontroler próbuje odna leźć atrybut 1ibrary w zasięgu sesji. Jeśli taki atrybut nie istnieje, automatycznie tworzy bibliotekę i umieszcza ją w zasięgu sesji. W kolejnym kroku kontroler uzyskuje dostęp do właściwości selectedBook biblioteki, aby sprawdzić, czy wybrano już jakąś książkę. Jeśli tak, kontroler przypisuje wybraną książkę atrybutowi sesji nazwanemu book. W razie braku wybranej książki kontroler przypisuje atrybutowi book wartość właściwą dla Piotrusia Pana. Kod tego kontrolera przedstawiono na listingu 8.20 w dalszej części tego punktu.
314
Rozdział 8. ■ Podwidoki i pakiet Apache Tiles
JavaServer Faces Na rysunku 8.8 przedstawiono strukturę katalogów aplikacji biblioteki. Dla uproszczenia pominięto w prezentowanej strukturze pliki H T M L z treścią oferowanych książek.
Rysunek 8.8. Struktura katalogów aplikacji biblioteki
£3 WEB-INF f -£3 classes | t (¡3 com I T £3 corejsf | t util j | ! Q Messages.class !
f Q Book, class
|
F Q LibraryTHeController.class
i
! Q messages.properties
T D Library, class y ( 3 rtb i 1 Q tiles-core-SNAPSHOT.jar j- Q faces-config.xmf — Q tiles.xml ■ Q web.xml (¡3 books f [¡3 alicelnWonderland
| r-Dj /
Q cheshire.jpg [¡3 peterpan
I j- D |
••
! D peterpan.jpg
Kod zawarty w plikach widocznych na rysunku 8.8 (z wyjątkiem plików H T M L) przedstawiono na listingach od 8.13 do 8.27. Analizując te listingi, warto zwrócić uwagę na łatwość, z jaką możemy dodawać do biblioteki nowe książki — aby utworzyć i dodać do istniejącej struktury nową książkę, wystarczy zmodyfikować konstruktor w pliku Library.java (patrz listing 8.19).
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..
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:
van v = p a n s e ln t( chi s v a lu e ) + incrément. i f ( isNaN( v ) ) re tu rn .
Zwraca wyrażenie reprezentujące wartość odpowiadającą danej nazwie.
UJ
if ('min' in tnis && v < this.mm) return, lf ('ma*1 in th1S && v > this.max) return, ihis value = v:
javax.faces.context.FacesContext
■ ELContext getELContext() (JS F 1.2)
language= "JavaScM pt">
document formst ' _ l d r ] [ '_ id l:m o n tn S p in n e r'] spin -= fun ctio n (in crem ent) {
idocument fo rm s[ 1_ i d l ' ] [ ’_ i d 1 : month S p in n e r’ ] .min
Zwraca kontekst języka wyrażeń.
UJ ■
javax.el.ValueExpression (JSF 1.2) Class getType(ELContext context)
= 0.
Zwraca typ danego wyrażenia reprezentującego wartość.
UJ ■
javax. faces. appli cati on.Appli cati on Converter createConverter(Class targetClass)
Tworzy konwerter na podstawie jego klasy docelowej. Implementacje JS F utrzymują mapę prawidłowych typów konwerterów, które z reguły określamy w ramach plików konfiguracyjnych naszych aplikacji. Jeśli za pośrednictwem argumentu targetClass przekażemy klucz któregoś z elementów tej mapy, metoda createConverter utworzy i zwróci egzemplarz odpowiedniego konwertera (reprezentowanego przez wartość właściwą dla klucza targetCl ass).
Kiedy piszemy kod języka JavaScript, który ma uzyskiwać dostęp do pól jakiegoś formularza, musimy dysponować identyfikatorem tego formularza (w tym przypadku '_ id l ' ): document fo rm s['
d 1 ' ] [ ' _ i d l •month Sp in n e r' ]
Funkcję drugiego indeksu tablicy pełni identyfikator klienta danego komponentu. Uzyskiwanie identyfikatora formularza jest zadaniem dość typowym, stąd decyzja o umiesz czeniu w klasie com.corejsf u t 11.Renderers metody pomocniczej odpowiedzialnej za tę operację:
370
JavaServer Faces
Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące
} W tym miejscu nie będziemy szczegółowo analizowali technik programowania w języku JavaScript; warto jednak zwrócić uwagę na ryzyko występowania konfliktów nazw (wsku tek umieszczania funkcji globalnych tego języka w kodzie nieznanej strony). Okazuje się, że język JavaScript zaprojektowano z należytą ostrożnością i kładąc szczególny nacisk na elastyczność modelu obiektowego. Zamiast pisać funkcję globalną spin, możemy ją zdefi niować jako metodę obiektu pola tekstowego. Język JavaScript umożliwia nam rozszerzanie możliwości obiektów „w locie” przez dodawanie nowych metod i pól. Właśnie tę technikę zastosujemy dla wartości minimalnych i maksymal nych komponentu datownika (reprezentowanych — w razie konieczności — odpowiednio przez pola min i max). Nową wersję kodu klasy odpowiedzialnej za wizualizację komponentu datownika przed stawiono na listingu 9.15.
Listing 9.15. Kod klasy spinner-js/src/java/com/corejsf/JSSpinnerRenderer.java 1. package com.corejsf; 2
Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące
spinner.setVal icKtrue):
373
h:coirimandLink. W tym podrozdziale opracujemy komponent wielokrotnego użytku, który twórcy stron będą mogli bez trudu stosować w swoich projektach.
} }
Komponent panelu podzielonego na zakładki ma kilka szczególnie interesujących cech: Warto pamiętać, że wprowadzone zmiany nie mają najmniejszego wpływu na komponent UISpi nner. Aktualizacji wymaga wyłącznie klasa odpowiedzialna za wizualizację, co ponownie dowodzi skuteczności kroku polegającego na wyodrębnieniu mechanizmu wizualizacji.
Stosowanie komponentów i facet potomnych Komponent datownika, który omówiono w pierwszej części tego rozdziału, jest na tyle pro sty, że nie może skutecznie ilustrować szeregu ważnych i przydatnych technik implementacji komponentów niestandardowych. Szczegółowa analiza tych metod wymaga posłużenia się przykładem nieporównanie bardziej skomplikowanego komponentu — będzie to komponent panelu podzielonego na zakładki (patrz rysunek 9.9).
Rysunek 9.9. Komponent panelu podzielonego na zakładki
■ Możemy stosować klasy CSS zarówno dla całego panelu podzielonego na zakładki, jak i dla poszczególnych zakładek (zaznaczonych lub nie). ■ Zakładki definiujemy za pomocą znaczników f : sel ect Item (lub f : sel ect Items), a więc zgodnie ze schematem znanym ze standardowych znaczników menu i list JS F (gdzie w ten sposób definiowano dostępne opcje). ■ Zawartość panelu podzielonego na zakładki zdefiniujemy za pośrednictwem facety (prezentowanej przez mechanizm odpowiedzialny za wizualizację). Możemy na przykład określić zawartość dla zakładki Washington (patrz rysunek 9.9) jako W ashington. Mechanizm wizualizacji odnajduje następnie facetę właściwą dla zakładki nazwanej W a s h in g to n . Opisywany model korzystania z facet przypomina sposób, w jaki stosowaliśmy facety h e a d e r i f o o t e r w ramach znacznika h: da ta T a b le . ■ Do panelu podzielonego na zakładki możemy dołączyć metodę nasłuchującą akcji. Taka metoda będzie powiadamiana za każdym razem, gdy użytkownik zaznaczy którąś z zakładek.
http://loc»lho^8080/
■ Tekst zakładek można łatwo lokalizować, stosując klucze z pakietu komunikatów (zamiast stałych łańcuchowych przypisywanych bezpośrednio zakładkom).
iB.oosevelti iLincoln; iWashmgor Thomas Jefferson, trzeci prezydent Stanów Zjednoczonych, urodził się w roku 1743 w stanie Wirginia Jefferson był ' -■ '- ' - ' ' ' -J ktor/u. zastąp ,r —-----------------------urząd prezydi | $H; H g został w 1801 |
r
ŚJeffersonj
nno7,IOi41h(Is;:ir,So.';4buede*ne.!nde..f«e,
iŁmcota] jWashingtonj
¿skończono
Theodore Roosevelt był 26 prezydentem Stanów Zjednoczonych. Został prezydentem w roku 190] po; zamachu na prezydenta McKinley a TJrząd prezydenta objął w wieku 42 lał i przeszedł do histora jako j
■ Nasz panel podzielony na zakładki wykorzystuje pola ukryte do przesyłania pomiędzy klientem a serwerem informacji o zaznaczonej zakładce i jej zawartości. Tak duża złożoność panelu podzielonego na zakładki sprawia, że możemy wykorzystywać ten komponent na wiele różnych sposobów. Przykład prostego zastosowania tego komponentu przedstawiono poniżej: < co rejsf labbecIPane >
Prezydent Lmcoln przeszedł do łustomjako Wielki Łmancypator, ponieważ w ogromnym stopmu przyczynił się do anęsiema niewolnictwa na terem? Stanów Zjednoczonych. Urodzony w 1809 roJ wychował dwn«^odridon«K na zakiadki -MozKla F«efóx'' " 1860 ZąnaŁl L 1• -- - ■ | U )A Jd y c ja
CQim'rnJ
f i A '-
JJ/idok
-
#
Hi*om
kj
|||g
f j f [.
I
hWm.
,j\ '
httP »omno f 8030 rapaeapanH/.nci^xr<,ce<
s e le c l 1rena itemLabel = "Jeffenso n"
item vaiu e= "jefferso n "/>
s e le c t (tem i temLabel =” Roosevel i "
ite m V a iu e= "ro o seve lt’7>
s e le c t Item itemLabel ="Lm c o ln"
item value= "1m coln"/>
s e le c t item uewLabei ="washm gton" item vaipe="washmgton'7>
<1■ facet
"t
nam e="jefferson">
±&mz
5SS53
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:
Przetwarzanie znaczników potomnych typu Selectltem Panel podzielony na zakładki oferuje możliwość definiowania zakładek za pośrednictwem znaczników f: selectltem lub f : sel ect Items. Wspomniane znaczniki tworzą komponenty typu UlSelectltem, po czym dodają je do komponentu panelu podzielonego na zakładki jako jego komponenty potomne. Ponieważ mechanizm odpowiedzialny za wizualizację naszego komponentu musi wizualizować także te komponenty potomne, powinien nadpisywać metody rendersChildrenO i encodeChildren(): public boolean rendersChildren() { return true,
}
public void encodeChildren(FacesContext context, UlComponent component) throws java.io.IOException { / / ta metoda zostanie wywołana, nawet jeśli komponent panelu podzielonego 11 na zakładki nie ma komponentów potomnych i f (component.getChildCountO == 0) { return:
}
Warto zwrócić uwagę na etykiety elementów (zakładek), które mają postać kluczy wska zujących na odpowiednie pary w pakiecie komunikatów: jeffersonTabText=Jefferson rooseveltTabText=Roosevelt 1i ncolnTabText=Li ncol n washi ngtonTabText=Washington
List items = com.corejsf.util.Renderers.getSelectItems(context, component), Iterator it = items iteratorO; while (it.hasNextO) encodeTab(context, writer, (Selectltem) it.nextO, component);
376
JavaServer Faces Ogólnie komponent przetwarzający swoje komponenty potomne musi obejmować następujący fragment kodu: Iterator children = component.getChi 1dren() iteratorO. while (children.hasNextO) { UlComponent child = (UlComponent) children.nextO. processChild(context, writer, child, component).
Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące
377
Metoda encodeEnd sprawdza, czy treść aktualnie zaznaczonej zakładki odpowiada nazwie facety danego komponentu. Jeśli tak, przystępuje do kodowania tej facety przez wywołanie jej metod encodeBegin, encodeChi 1dren i encodeEnd. Za każdym razem, gdy mechanizm w i zualizacji przetwarza swoje obiekty potomne, automatycznie musi przejmować odpowie dzialność za realizację opisanych zadań.
EBJ javax.faces.component.UlComponent
} Z drugiej strony, nasza sytuacja jest bardziej skomplikowana. W ielu Czytelników zapewne pamięta z rozdziału 4., że wartością znacznika f : sel ect Items może być pojedynczy ele ment, kolekcja elementów, tablica elementów lub mapa obiektów Javy. Za każdym razem, gdy nasza klasa przetwarza obiekty potomne typów Selectltem lub Selectltems, musimy uwzględniać wszystkie możliwe kombinacje wymienionych możliwości. Metoda com.corejsf.util .Renderers.getSelectltems uwzględnia wszystkie te typy danych i na ich podstawie tworzy listę obiektów klasy Selectltem. Kod tej metody pomocniczej można znaleźć na listingu 9.12 we wcześniejszej części tego rozdziału. Metoda encodeChi 1dren klasy TabbedPaneRenderer wywołuje metodę getSelectltems i ko duje kolejno wszystkie komponenty potomne do postaci zakładek. Szczegóły tego mecha nizmu zostaną omówione w punkcie „Stosowanie pól ukrytych” w dalszej części tego pod rozdziału.
Przetwarzanie facet Panel podzielony na zakładki wykorzystuje nazwy facet identyfikacji treści właściwej dla poszczególnych zakładek. Metoda encodeEnd odpowiada za wizualizację wybranej facety: public void encodeEnd(FacesContext context, UlComponent component) throws java io.IOException { ResponseWriter writer = context.getResponseWriter(); UITabbedPane tabbedPane = (UITabbedPane) component, String content = tabbedPane.getContent(), i f (content != nul1) { UlComponent facet = component.getFacet(content); i f (facet != nul1) { i f (facet.isRenderedO) { facet,encodeBegin(context), i f (facet.getRendersChildrenO) facet.encodeChiIdren(context); facet.encodeEnd(context):
■ UlComponent getFacet(String facetName) Zwraca referencję do danej facety (jeśli taka faceta istnieje). Jeśli dana faceta nie istnieje, metoda getFacet zwraca wartość riul 1. ■ boolean getRendersChildrenO Zwraca wartość typu bool ean. Jeśli dany komponent odpowiada za wizualizację swoich potomków, zwraca true; w przeciwnym razie metoda getRendersChi 1dren zwraca fal se. Jeśli metoda getRendersChi 1dren zwróci wartość fal se, metoda encodeChi 1dren danego komponentu w ogóle nie zostanie wywołana. Metoda getRendersChi 1dren domyślnie zwraca właśnie wartość tai se. ■ boolean isRenderedO Zwraca wartość właściwości rendered. Komponent jest wizualizowany tylko wtedy, gdy wspomniana właściwość ma wartość true.
Kodowanie stylów CSS Obsługa stylów C SS jest procesem dwuetapowym: 1. Należy dodać odpowiedni atrybut do deskryptora biblioteki znaczników (T LD ).
2. Należy zakodować atrybut danego komponentu w metodzie encode klasy odpowiedzialnej za wizualizację. W pierwszej kolejności dodajemy atrybuty styleClass, tabCl ass i sel ectedTabCl ass do pliku deskryptora TLD : styleClassStyl CSS stosowany dla tego komponentu
} Klasa UITabbedPane definiuje pole content reprezentujące nazwę facety lub adres U R L aktualnie wyświetlanej zakładki.
W kolejnym kroku powinniśmy napisać atrybuty dla klas CSS: public class TabbedPaneRenderer extends Renderer {
378
Rozdział 9. ■ Niestandardowe komponenty, konwertery i mechanizmy weryfikujące
JavaServer Faces public void encodeBegin(FacesContext context, UlComponent component) throws J a v a .io.IOException { ResponseWriter writer = context,getResponseWriter(), writer.startElementC'table", component); String styleClass = (String) component.getAttributesO.get("styleClass"), i f (styleClass != nul1) writer wniteAttributeC’class", styleClass, "styleClass"): wri ter .wri te( "\n"): // w ten sposób poprawiamy czytelność generowanego kodu HTML-a
}
public void encodeChildren(FacesContext context, UlComponent component) throws Java.i o .IOException { encodeTab(context, responseWriter, selectltem, component),
} private void encodeTab(FacesContext context, ResponseWriter writer, Selectltem item. UlComponent component) throws java.io.IOException { String tabText = getLocalizedTabText(component, item.getLabel()), String tabClass = n u ll, i f (content.equa1s(selectedContent)) tabClass = (String) component.getAttributesO .getC'selectedTabClass"), else tabClass = (String) component.getAttributesO.getC'tabClass"); i f (tabClass != nul1) writer.writeAttributeOclass", tabClass, "tabClass"):
379
informacjami o wybranej zakładce. Odpowiednie dane są składowane w polu ukrytym znajdującym się za wszystkimi zakładkami:
Oprócz danych formularza na serwer jest odsyłana także nazwa i wartość tego pola ukrytego, dzięki czemu metoda decode może aktywować właściwą zakładkę. Metoda encodeTab klasy odpowiedzialnej za wizualizację generuje znaczniki hiperłącza. Metoda encodeEnd wywołuje metodę encodeHiddenFields, która z kolei koduje nasze pole ukryte. Szczegółowe elementy opisywanego mechanizmu można znaleźć na listingu 9.17 w dalszej części tego rozdziału. Kiedy klasa odpowiedzialna za wizualizację komponentu panelu podzielonego na zakładki dekoduje otrzymane żądanie, wykorzystuje parametr żądania właściwy dla naszego pola ukrytego do określenia zawartości tego komponentu: public void decode(FacesContext context, UlComponent component) { Map requestParams = context.getExternalContext 0 .getRequestParameterMapO; String clientld = component.getClientld(context), String content = (String) ( requestParams.get(clientld)); i f (content != null && !content.equals("")) { UITabbedPane tabbedPane = (UITabbedPane) component, tabbedPane.setContent(content):
}
} ’
Zapisywanie i przywracanie stanu
} Atrybut styleClass kodujemy dla tabeli zewnętrznej panelu podzielonego na zakładki, na tomiast atrybuty tabClass i selectedTabClass kodujemy dla poszczególnych znaczników.
Kiedy nasza aplikacja zapisuje swój stan na serwerze, obiekty widoku są składowane w pamięci operacyjnej. Z drugiej strony, kiedy stan aplikacji jest zapisywany po stronie klienta, obiekty widoku są kodowane i składowane w polu ukrytym (w formie bardzo długiego łańcucha, którego przykład przedstawiono poniżej):