Darmowy fragment publikacji:
Tytuł oryginału: Mastering JavaServer Faces 2.2
Tłumaczenie: Piotr Rajca
ISBN: 978-83-283-2419-0
Copyright © 2014 Packt Publishing
First published in the English language under the title ‘Mastering JavaServer Faces 2.2
– (9781782176466)’.
Polish edition copyright © 2016 by Helion S.A.
All rights reserved.
All rights reserved. No part of this book may be reproduced or transmitted in any
form or by any means, electronic or mechanical, including photocopying, recording
or by any information storage retrieval system, without permission from the Publisher.
Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości
lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione.
Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie
książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie
praw autorskich niniejszej publikacji.
Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi
bądź towarowymi ich właścicieli.
Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte
w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej
odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne
naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION
nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe
z wykorzystania informacji zawartych w książce.
Wydawnictwo HELION
ul. Kościuszki 1c, 44-100 GLIWICE
tel. 32 231 22 19, 32 230 98 63
e-mail: helion@helion.pl
WWW: http://helion.pl (księgarnia internetowa, katalog książek)
Pliki z przykładami omawianymi w książce można znaleźć pod adresem:
ftp://ftp.helion.pl/przyklady/jsf22m.zip
Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://helion.pl/user/opinie/jsf22m
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.
Printed in Poland.
• Kup książkę
• Poleć książkę
• Oceń książkę
• Księgarnia internetowa
• Lubię to! » Nasza społeczność
Spis treści
O autorze
O recenzentach
Wstęp
Rozdział 1. Dynamiczny dostęp do danych aplikacji JSF
przy użyciu Expression Language (EL 3.0)
Składnia EL
Operatory EL
Hierarchia operatorów EL
Zastrzeżone słowa EL
Przetwarzanie natychmiastowe oraz opóźnione
Wyrażenia wartościowe EL
Odwołania do komponentów zarządzanych
Odwołania do właściwości komponentów zarządzanych
Odwołania do zagnieżdżonych właściwości komponentów zarządzanych
Odwołania do typów wyliczeniowych Java SE
Odwołania do kolekcji
Niejawne obiekty EL
Wyrażenia odwołujące się do metod
Teksty warunkowe w JSF
Pisanie własnego mechanizmu przetwarzającego
Przegląd EL 3.0
Stosowanie operatora przypisania
Stosowanie operatora konkatenacji
Stosowanie operatora średnika
Poznawanie wyrażeń lambda
Stosowanie obiektów kolekcji
Podsumowanie
11
13
15
19
20
20
21
21
22
22
23
24
25
27
27
29
31
33
37
45
45
45
46
46
47
49
Poleć książkęKup książkę
JavaServer Faces 2.2. Mistrzowskie programowanie
Rozdział 2. Komunikacja w JSF
Przekazywanie i pobieranie parametrów
Stosowanie parametrów kontekstu
Przekazywanie parametrów żądania przy użyciu znacznika f:param
Stosowanie parametrów widoku
Wywoływanie akcji z wykorzystaniem żądań GET
Przekazywanie atrybutów przy użyciu znacznika f:attribute
Ustawianie wartości właściwości przy użyciu obiektów nasłuchujących akcji
Przekazywanie parametrów przy użyciu zasięgu Flash
Zastępowanie znacznika f:param znacznikiem JSTL c:set
Przesyłanie danych w ciasteczkach
Stosowanie pól ukrytych
Przesyłanie haseł
Programowy dostęp do atrybutów komponentów interfejsu użytkownika
Przekazywanie parametrów przy użyciu wyrażeń odwołujących się do metod
Komunikacja przy użyciu atrybutu binding
Komunikacja pomiędzy komponentami zarządzanymi
Wstrzykiwanie jednego komponentu zarządzanego do drugiego
Komunikacja pomiędzy komponentami zarządzanymi
przy użyciu mapy aplikacji lub sesji
Programowy dostęp do innych komponentów zarządzanych
Podsumowanie
Rozdział 3. Zasięgi JSF — długość życia i zastosowanie
w komunikacji komponentów zarządzanych
Zasięgi JSF a zasięgi CDI
Zasięg żądania
Zasięg sesji
Zasięg widoku
Zasięg aplikacji
Zasięg konwersacji
Zasięg przepływu
Prosty przepływ
Przepływy z komponentami
Przepływy zagnieżdżone
Programowe konfigurowanie przepływów
Przepływy a przypadki nawigacji
Badanie przypadków nawigacji w przepływach
Stosowanie metod initializer i finalizer
Przełączanie przepływu
Pakowanie przepływów
Programowy zasięg przepływu
Zależny pseudozasięg
Zasięg none
Zasięg niestandardowy
Implementacja klasy zasięgu niestandardowego
Wyznaczanie wyrażeń EL zasięgów niestandardowych
4
51
52
52
52
55
62
66
69
71
75
76
78
79
79
80
81
83
83
85
86
87
89
90
92
95
98
100
102
105
108
112
114
118
120
123
124
126
129
130
133
134
134
135
136
Poleć książkęKup książkę
Spis treści
Kontrola czasu istnienia zasięgu przy użyciu obiektu nasłuchującego akcji
Kontrola czasu istnienia zasięgu niestandardowego z użyciem obiektów
NavigationHandler
Tworzenie instancji komponentów zarządzanych
Wstrzykiwanie komponentów
Podsumowanie
Rozdział 4. Konfigurowanie JSF przy użyciu plików XML i adnotacji — część 1.
Nowe przestrzenie nazw JSF 2.2
Programowa konfiguracja w JSF 2.2
Konfigurowanie komponentów zarządzanych w XML-u
Stosowanie wielu plików konfiguracyjnych
Konfiguracja ustawień lokalnych i wiązek zasobów
Konfiguracja walidatorów i konwerterów
Konfigurowanie nawigacji
Nawigacja niejawna
Nawigacja warunkowa
Nawigacja z wywłaszczeniem
Nawigacja programowa
Konfigurowanie obiektów nasłuchujących akcji
Obiekty nasłuchujące akcji aplikacji
Konfigurowanie metod nasłuchujących zdarzeń systemowych
Stosowanie znacznika f:event
Implementacja interfejsu SystemEventListener
Konfigurowanie metod nasłuchujących faz
Stosowanie adnotacji @ListenerFor oraz @ListenersFor
Podsumowanie
Rozdział 5. Konfigurowanie JSF przy użyciu plików XML i adnotacji — część 2.
Konfiguracja obiektów obsługi zasobów
Programowe dodawanie zasobów CSS i JS
Konfiguracja obiektu obsługi widoków
Przesłanianie mechanizmów wizualizacji JSF
Stosowanie operacji wykonywanych po stronie klienta
Klasy wytwórcze JSF
Konfiguracja globalnego obiektu obsługi wyjątków
Konfiguracja klasy wytwórczej RenderKit
Konfiguracja PartialViewContext
Konfiguracja obiektu VisitContext
Konfiguracja obiektów ExternalContext
Konfiguracja Flash
Window ID API w JSF 2.2
Konfigurowanie cyklu życia
Konfigurowanie aplikacji
Konfigurowanie VDL
Połączone możliwości wielu klas wytwórczych
Podsumowanie
139
141
144
144
147
149
150
151
152
157
159
161
169
169
172
175
177
178
180
183
183
185
191
195
196
197
198
205
205
209
215
219
220
223
224
227
230
233
235
241
244
246
248
249
5
Poleć książkęKup książkę
JavaServer Faces 2.2. Mistrzowskie programowanie
Rozdział 6. Korzystanie z danych tabelarycznych
Tworzenie prostej tabeli JSF
Klasa CollectionDataModel JSF 2.2
Sortowanie tabel
Sortowanie i DataModel — klasa CollectionDataModel
Usuwanie wiersza tabeli
Edycja i aktualizacja wierszy tabeli
Dodawanie nowych wierszy
Wyświetlanie numerów wierszy
Wybieranie pojedynczego wiersza
Wybieranie wielu wierszy
Zagnieżdżanie tabel
Podział tabel na strony
Generowanie tabel przy użyciu API JSF
Filtrowanie tabel
Określanie wyglądu tabel przy użyciu stylów
Zmiana koloru tła wierszy z użyciem atrybutu rowClasses
Podświetlanie wiersza wskazanego myszą
Podświetlanie wierszy po kliknięciu myszą
Podsumowanie
Rozdział 7. JSF i AJAX
Krótki przegląd cyklu życia JSF-AJAX
Prosty przykład JSF-AJAX na dobry początek
Atrybuty JSF-AJAX
Atrybuty execute oraz render
Atrybut listener
Atrybut event
Atrybut onevent — monitorowanie stanu AJAX-a po stronie klienta
Atrybut onerror — monitorowanie błędów AJAX-a po stronie klienta
Grupowanie komponentów w znaczniku f:ajax
Zastosowanie AJAX-a do aktualizacji pól formularzy po wystąpieniu błędów walidacji
Przyciski Anuluj i Wyczyść
Łączenie AJAX-a i zasięgu przepływu
Żądania zwrotne i AJAX
Warunkowe wyświetlanie i przetwarzanie żądań zwrotnych
Czy to nie jest żądanie AJAX?
AJAX i znacznik f:param
Kontrola kolejki żądań AJAX
Jawne wczytywanie pliku jsf.js
Prezentacja wartości parametrów
Metoda jsf.ajax.request i komponenty inne niż UICommand
Dostosowywanie zawartości pliku jsf.js
Implementacja AJAX-owego paska postępów (sygnalizator działania)
Podsumowanie
6
251
252
254
259
265
267
269
272
274
275
277
279
280
286
291
296
296
297
298
299
301
302
302
303
304
306
307
308
309
311
312
314
318
322
324
327
328
329
330
331
332
335
338
340
Poleć książkęKup książkę
Spis treści
Rozdział 8. JSF 2.2 — HTML5 i przesyłanie plików na serwer
Korzystanie z HTML5 i JSF 2.2
Atrybuty przekazywane
Elementy przekazywane
JSF 2.2 — HTML5 i model Bean Validation 1.1 (Java EE 7)
Mechanizm przesyłania plików w JSF 2.2
Prosty przykład przesyłania plików z wykorzystaniem możliwości JSF 2.2
Stosowanie wielu elementów h:inputFile
Pobieranie informacji o przesyłanym pliku
Zapis przesłanych danych na dysku
Walidator przesyłanych plików
Przesyłanie plików z użyciem AJAX-a
Przesyłanie plików z podglądem
Przesyłanie większej liczby plików
Przesyłanie plików i nieokreślony pasek postępów
Przesyłanie plików i określony pasek postępów
Podsumowanie
Rozdział 9. Zarządzanie stanem w JSF
Zapisywanie stanu widoku w JSF
Częściowe zapisywanie stanu widoku
Częściowe zapisywanie stanu i przeglądanie drzewa
Zapisywanie stanu widoku na serwerze lub kliencie
Logiczne i fizyczne widoki JSF
Zapisywanie stanu w bazie danych — aplikacja eksperymentalna
Obsługa wyjątków ViewExpiredException
Serializacja stanu w sesji na serwerze
JSF 2.2 jest technologią bezstanową
Widoki bezstanowe oraz komponenty umieszczane w zasięgu widoku
Programowe wykrywanie widoków bezstanowych
Uwagi dotyczące bezpieczeństwa JSF
Cross-site request forgery (CSRF)
Cross-site scripting (XSS)
Wstrzykiwanie SQL
Podsumowanie
Rozdział 10. Niestandardowe komponenty JSF
Tworzenie komponentów niestandardowych, które nie są komponentami złożonymi
Tworzenie własnego obiektu obsługi znacznika
Tajniki konstrukcji komponentów niestandardowych
Tworzenie komponentów złożonych
Implementacja komponentu złożonego Temperature
Przekształcanie komponentu jQuery w komponent złożony
Pisanie pola do wyboru dat HTML5 jako komponentu złożonego
Wzbogacanie obrazka o akcje
Stosowanie facet złożonych
Walidacja lub konwersja danych wejściowych w komponentach złożonych
341
341
342
344
346
347
348
350
351
353
355
356
357
364
366
368
371
373
373
374
374
375
378
379
386
389
391
392
394
395
395
395
396
396
397
398
401
402
413
416
420
425
429
431
433
7
Poleć książkęKup książkę
JavaServer Faces 2.2. Mistrzowskie programowanie
Sprawdzanie obecności atrybutu
Niebezpieczeństwa stosowania komponentów złożonych
Ukrywanie atrybutów przekazywanych w komponentach złożonych
Rozpowszechnianie komponentów złożonych w postaci plików JAR w JSF 2.2
Dodawanie komponentów złożonych w sposób programowy
Podsumowanie
Rozdział 11. Kontrakty biblioteki zasobów JSF 2.2 — motywy
Stosowanie kontraktów
Określanie wyglądu tabel przy użyciu kontraktów
Stosowanie kontraktów do określania wyglądu komponentów interfejsu użytkownika
Kontrakty stylów stosowane na urządzeniach różnych typów
Tworzenie kontraktów dla komponentów złożonych
Implementacja przełącznika motywów
Konfiguracja kontraktów w kodzie XML
Pakowanie kontraktów w plikach JAR
Podsumowanie
435
435
436
439
441
443
445
446
448
451
453
458
460
467
468
468
Rozdział 12. Szablony technologii Facelets
471
471
Krótka prezentacja znaczników technologii Facelets
474
Tworzenie prostego szablonu — PageLayout
Przekazywanie parametrów przy użyciu znacznika ui:param
477
Przekazywanie właściwości komponentów i metod akcji przy użyciu znacznika ui:param 479
481
Stosowanie znaczników ui:decorate oraz ui:fragment
Iteracja przy użyciu znacznika ui:repeat
484
487
Stosowanie znaczników ui:include oraz f:viewParam
489
Stosowanie znaczników ui:include oraz ui:param
491
Debugowanie z użyciem znacznika ui:debug
492
Usuwanie zawartości przy użyciu znacznika ui:remove
Stosowanie atrybutu jsfc
493
494
Rozszerzanie szablonu PageLayout
499
Programowe aspekty faceletów
499
499
502
506
507
508
510
510
511
512
512
Zagadnienia związane z klasą FaceletFactory
Stosowanie klasy FaceletCache
Klasa ResourceResolver zastąpiona klasą ResourceHandler
Programowe dołączanie faceletów
Tworzenie klasy TagHandler
Pisanie niestandardowych funkcji bibliotek znaczników faceletów
Podsumowanie
Pułapki stosowania faceletów
AJAX i znacznik ui:repeat
Przykład użycia znaczników c:if oraz ui:fragment
Przykład użycia znaczników c:forEach oraz ui:repeat
Dodatek A. Cykl życia JSF
Skorowidz
8
515
517
Poleć książkęKup książkę8
JSF 2.2 — HTML5
i przesyłanie plików
na serwer
Niniejszy rozdział można podzielić na dwie główne części. Pierwsza z nich jest poświęcona
wsparciu dla języka HTML5 dostępnemu w JSF 2.2, druga natomiast opisuje nowe mechani-
zmy przesyłania plików na serwer dodane w JSF 2.2. Oczywiście obie te części nie są ze sobą
bezpośrednio powiązane, jednak, jak się przekonasz, komponenty do przesyłania plików do-
dane w JSF 2.2 można uatrakcyjnić, korzystając z nowych narzędzi wsparcia dla HTML5, a nowe
atrybuty przekazywane (ang. pass-through attributes) mogą być bardzo przydatne w przypad-
ku rozszerzania komponentu do przesyłania plików dodanego w JSF 2.2 przy użyciu nowych
możliwości analogicznego komponentu języka HTML5.
Korzystanie z HTML5 i JSF 2.2
Każdy, kto zajmuje się tworzeniem aplikacji internetowych, bardzo entuzjastycznie podcho-
dzi do poznawania i stosowania języka HTML5 udostępniającego nowy zestaw znaczników
i możliwości, takich jak: audio , video , keygen itd. Począwszy od wersji 2.2, programiści
JSF mogą korzystać z możliwości języka HTML5, używając:
atrybutów przekazywanych;
elementów przekazywanych (ang. pass-through elements; czyli kodu
znacznikowego przyjaznego dla języka HTML).
Poleć książkęKup książkęJavaServer Faces 2.2. Mistrzowskie programowanie
Choć elementy i znaczniki przekazywane są inspirowane możliwościami języka HTML5, to jednak są one
elementami JSF, których można używać także z innymi wersjami standardu HTML.
Mechanizmy te stanowią alternatywę dla pisania własnych zestawów mechanizmów wizualizacji.
To doskonałe rozwiązanie, gdyż język HTML5 wciąż jest w trakcie rozwoju, a to oznacza, że
pisanie i dostosowywanie zestawów mechanizmów wizualizacji do bezustannych zmian języ-
ka może stanowić trudne wyzwanie.
Aby móc korzystać z języka HTML5 w JSF 2.0, konieczne będzie napisanie własnych zestawów
mechanizmów wizualizacji, które będą obsługiwać nowe elementy i atrybuty tego języka.
Atrybuty przekazywane
Począwszy do JSF 2.2, dostępne są atrybuty przetwarzane przez komponenty JSF na serwe-
rze oraz atrybuty przekazywane (ang. pass-through attributes), które są przetwarzane w trak-
cie działania aplikacji po stronie klienta.
Wygodnym elementem HTML5, którego można użyć do przeprowadzenia testów atrybutów
przekazywanych, jest input . Do jego nowych możliwości można zaliczyć między innymi:
nowe wartości atrybutu type (na przykład: email, tel, color oraz reset) oraz nowy atrybut
placeholder (zawiera on tekst podpowiedzi wyświetlanej w pustym polu).
W kodzie HTML5 element ten można zapisać w następujący sposób:
input placeholder= Wpisz adres e-mail zawodnika type= email
Korzystając z atrybutów przekazywanych, ten sam kod można wygenerować na pięć różnych
sposobów:
Umieścić atrybuty przekazywane w nowej przestrzeni nazw http://xmlns.jcp.org/
jsf/passthrough (każdy programista JSF wie, czym są przestrzenie nazw oraz
elementy z prefiksami. Stosowanie zarówno przestrzeni nazw, jak i elementów
z prefiksami nie jest niczym szczególnym ani tajemniczym). Przekonajmy się
zatem, w jaki sposób można wygenerować powyższy element HTML5, używając
atrybutów przekazywanych JSF:
html xmlns= http://www.w3.org/1999/xhtml
xmlns:h= http://xmlns.jcp.org/jsf/html
xmlns:f5= http://xmlns.jcp.org/jsf/passthrough
xmlns:f= http://xmlns.jcp.org/jsf/core
...
h:body
h:inputText value= #{playersBean.email} f5:type= email
f5:placeholder= Wpisz adres e-mail zawodnika /
...
342
Poleć książkęKup książkęRozdział 8. • JSF 2.2 — HTML5 i przesyłanie plików na serwer
Podczas prac nad tą książką wciąż trwała debata, jakiego prefiksu należy używać dla tej przestrzeni
nazw. Początkowo wybrano prefiks p, jednak jest on powszechnie uznawany za prefiks biblioteki PrimeFaces,
dlatego konieczne jest użycie innego. W tej książce zastosowałem prefiks f5, ale nic nie stoi na prze-
szkodzie, by zastąpić go dowolnym innym, który zwycięży w debatach i zyska największą popularność.
Skorzystać ze znacznika f:passThroughAttribute w sposób pokazany
w poniższym przykładzie:
h:inputText value= #{playersBean.email}
f:passThroughAttribute name= placeholder value= Wpisz adres e-mail
zawodnika /
f:passThroughAttribute name= type value= email /
/h:inputText
Atrybuty przekazywane mogą także pochodzić z komponentów zarządzanych.
W takim przypadku należy je umieścić w kolekcji Map String, String , przy
czym kluczami jej elementów są nazwy atrybutów, a wartościami — wartości tych
atrybutów. Oto przykład takiego rozwiązania:
private Map String, String attrs = new HashMap ();
...
attrs.put( type , email );
attrs.put( placeholder , Wpisz adres e-mail zawodnika );
W kodzie strony należy natomiast użyć znacznika f:passThroughAttributes ,
jak pokazano poniżej:
h:inputText value= #{playersBean.email}
f:passThroughAttributes value= #{playersBean.attrs} /
/h:inputText
W przypadku stosowania języka Expression Language 3 (wchodzącego w skład
środowiska Java EE 7) atrybuty można także definiować bezpośrednio, w sposób
przedstawiony w poniższym przykładzie (w praktyce sprowadza się to do bezpośredniego
zdefiniowania kolekcji Map String, String przy użyciu wyrażenia EL 3):
h:inputText value= #{playersBean.email}
f:passThroughAttributes value= #{{ placeholder : Wpisz adres e-mail zawodnika ,
type : email }} /
/h:inputText
Kompletna implementacja takiego rozwiązania jest dostępna w kodach
dołączonych do książki jako aplikacja przyklad_8_01_1.
Atrybuty przekazywane można także dodawać programowo. Poniższy przykład
pokazuje, w jaki sposób można wygenerować pole tekstowe HTML5 i dodać je
do formularza:
h:body
h:form id= playerForm
...
343
Poleć książkęKup książkęJavaServer Faces 2.2. Mistrzowskie programowanie
/h:form
/h:body
...
FacesContext facesContext = FacesContext.getCurrentInstance();
UIComponent formComponent =
facesContext.getViewRoot().findComponent( playerForm );
HtmlInputText playerInputText = new HtmlInputText();
Map passThroughAttrs = playerInputText.getPassThroughAttributes();
passThroughAttrs.put( placeholder , Wpisz adres e-mail zawodnika );
passThroughAttrs.put( type , email );
formComponent.getChildren().add(playerInputText);
...
Kompletna implementacja takiego rozwiązania jest dostępna w kodach
dołączonych do książki jako aplikacja przyklad_8_01_02.
Elementy przekazywane
Programiści JSF ukrywają kod HTML pod postacią komponentów JSF. Dla twórców stron
WWW kod JSF może wyglądać dosyć dziwnie, jednak generowany na jego podstawie kod
HTML jest doskonale znany. W celu zmodyfikowania generowanego kodu HTML twórcy stron
WWW muszą modyfikować kod JSF, co może być dla nich stosunkowo trudnym zadaniem.
Na szczęście JSF 2.2 udostępnia wygodne rozwiązanie stworzone z myślą o języku HTML5.
Są to tak zwane elementy przekazywane (ang. pass-through elements). Korzystając z tego me-
chanizmu, projektanci i twórcy stron mogą pisać czysty kod HTML, programiści JSF nato-
miast mogą przejmować go i kojarzyć elementy HTML z kodem wykonywanym na serwerze,
dodając do nich lub zastępując odpowiednie atrybuty. JSF rozpoznaje takie atrybuty, jeśli na-
leżą one do przestrzeni nazw http://xmlns.jcp.org/jsf. Poniższy fragment kodu przedstawia
przykład strony JSF, która nie zawiera nawet jednego znacznika JSF:
html xmlns= http://www.w3.org/1999/xhtml
xmlns:jsf= http://xmlns.jcp.org/jsf
head jsf:id= head
title /title
/head
body jsf:id= body
form jsf:id= form
Imię: input type= text jsf:value= #{playersBean.playerName} /
Nazwisko: input type= text jsf:value= #{playersBean.playerSurname} /
button jsf:action= #{playersBean.playerAction()} Pokaż /button
/form
/body
/html
344
Poleć książkęKup książkęRozdział 8. • JSF 2.2 — HTML5 i przesyłanie plików na serwer
JSF przegląda elementy HTML w poszukiwaniu atrybutów należących do przestrzeni nazw http://
xmlns.jcp.org/jsf. Dla każdego elementu zawierającego takie atrybuty JSF określi jego typ i zastąpi go
odpowiednim komponentem JSF (na przykład znacznik head zostanie zastąpiony znacznikiem
h:head , a input znacznikiem h:inputText ). JSF doda komponenty do drzewa komponentów,
które zostanie wyświetlone w przeglądarce jako kod HTML. Taki komponent JSF zostanie powiązany
z konkretnym elementem, a jego atrybuty, w zależności od pochodzenia, zostaną określone na podstawie
„normalnych” atrybutów lub atrybutów przekazywanych. Informacje na temat odwzorowywania komponen-
tów JSF na elementy HTML można znaleźć na stronie http://docs.oracle.com/javaee/7/api/javax/faces/
view/facelets/TagDecorator.html. W przypadku elementów HTML, które nie mają bezpośrednich odpo-
wiedników (takich jak: div lub span ), JSF utworzy specjalny komponent, rodzinę komponentów, taką
jak: javax.faces.Panel, oraz typ mechanizmu wizualizacji javax.faces.passthrough.Element;
informacje na ten temat można znaleźć na stronie http://docs.oracle.com/javaee/7/javaserver-faces-2-2/
vdldocs-facelets/jsf/element.html.
Kompletna implementacja takiego rozwiązania jest dostępna w kodach dołączonych do książ-
ki jako aplikacja przyklad_8_01_03.
Ponieważ JSF zastępuje elementy HTML komponentami JSF, elementów tych można używać
bez żadnych ograniczeń, co oznacza, że możemy korzystać z nich w kodzie JSF. Na przykład
można używać walidatorów, konwerterów oraz znaczników f:param , co pokazano w poniż-
szym przykładzie:
html xmlns= http://www.w3.org/1999/xhtml xmlns:jsf= http://xmlns.jcp.org/jsf
xmlns:f= http://xmlns.jcp.org/jsf/core
head jsf:id= head
title /title
/head
body jsf:id= body
form jsf:id= form
Imię:
input type= text jsf:value= #{playersBean.playerName}
f:validator validatorId= playerValidator /
/input
!-- albo w ten sposób --
input type= text jsf:value= #{playersBean.playerName}
jsf:validator= playerValidator /
Nazwisko:
input type= text jsf:value= #{playersBean.playerSurname}
f:validator validatorId= playerValidator /
/input
!-- albo w ten sposób --
input type= text jsf:value= #{playersBean.playerSurname}
jsf:validator= playerValidator /
button jsf:action= #{playersBean.playerAction()} Pokaż
f:param id= playerNumber name= playerNumberParam value= 2014 /
/button
345
Poleć książkęKup książkęJavaServer Faces 2.2. Mistrzowskie programowanie
/form
/body
/html
Kompletna implementacja takiego rozwiązania jest dostępna w kodach dołączonych do książ-
ki jako aplikacja przyklad_8_01_04.
JSF 2.2 — HTML5 i model Bean Validation 1.1 (Java EE 7)
Model Bean Validation 1.1 (patrz https://docs.oracle.com/javaee/7/tutorial/bean-validation001.htm)
może być idealnym sposobem sprawdzania poprawności informacji wpisywanych przez użyt-
kownika w aplikacjach tworzonych z użyciem JSF 2.2 i HTML5. Poniższy przykład pokazuje,
jak można sprawdzić poprawność imienia i nazwiska w komponencie PlayersBean — nie są
akceptowane wartości null, łańcuchy puste oraz łańcuchy krótsze od 3 znaków:
@Named
@RequestScoped
public class PlayersBean {
private static final Logger logger = Logger.getLogger(PlayersBean.
class.getName());
@NotNull(message = Imię nie może mieć wartości null ani być puste )
@Size(min = 3,message = Imię musi się składać z co najmniej 3 liter )
private String playerName;
@NotNull(message = Nazwisko nie może mieć wartości null ani być puste )
@Size(min = 3,message = Nazwisko musi się składać z co najmniej 3 liter )
private String playerSurname;
...
JSF może interpretować przesyłane puste łańcuchy znaków jako wartości null, jeśli w pliku
web.xml zostanie użyty poniższy parametr kontekstu:
context-param
param-name
javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL
/param-name
param-value true /param-value
/context-param
W tym przypadku nie ma zatem potrzeby, by stosować znacznik f:validator lub atrybut
validator. Kompletna wersja tego rozwiązania jest dostępna w kodach dołączonych do książki
jako aplikacja przyklad_8_02.
Biblioteka OmniFaces udostępnia zestaw mechanizmów wizualizacji dla języka HTML5, wyposażony
w możliwości stosowania jego nowych atrybutów. Więcej informacji na jego temat można znaleźć na
stronie http://showcase.omnifaces.org/.
346
Poleć książkęKup książkęRozdział 8. • JSF 2.2 — HTML5 i przesyłanie plików na serwer
Mechanizm przesyłania plików w JSF 2.2
Programiści JSF długo czekali na wbudowany komponent służący do przesyłania plików. Aż
do momentu pojawienia się wersji 2.2 JSF problem ten obchodzono, stosując rozszerzenia
JSF, takie jak PrimeFaces, RichFaces bądź też biblioteki, takie jak FileUpload należąca do
projektu Apache Commons.
W JSF 2.2 dodano jednak komponent przeznaczony do przesyłania plików na serwer (w kodzie
HTML jest on reprezentowany jako element input typu file). Komponent ten jest reprezen-
towany przez znacznik h:inputFile i można go stosować dokładnie tak samo jak wszystkie inne
komponenty JSF. Wyczerpujące informacje o wszystkich dostępnych atrybutach znacznika
h:inputFile można znaleźć w dokumentacji JSF, na stronie: http://docs.oracle.com/javaee/7/
javaserver-faces-2-2/vdldocs-facelets/h/inputFile.html; poniżej przedstawionych zostało jedy-
nie kilka najważniejszych spośród nich:
value: określa plik reprezentowany jako obiekt javax.servlet.http.Part, który
należy przesłać na serwer.
required: zawiera wartość logiczną. Przyjmuje wartość true, jeśli użytkownik
przed przesłaniem formularza musi określić wartość tego pola.
validator: określa walidator używany przez ten komponent.
converter: określa konwerter używany przez ten komponent.
valueChangeListener: ten atrybut określa metodę, którą należy wywołać
w przypadku zmiany wartości komponentu.
Komponent h:inputFile bazuje na specyfikacji Servlet 3.0, która wchodzi w skład platformy
Java EE od jej wersji 6. Specyfikacja Servlet 3.0 udostępnia mechanizm do przesyłania plików na
serwer bazujący na interfejsie javax.servlet.http.Part oraz adnotacji @MultipartConfig. Po-
niżej przedstawiony został prosty kod zgodny ze specyfikacją Servlet 3.0, pokazujący sposób
implementacji przesyłu plików; trzeba przy tym pamiętać, że jest to serwlet; zastosujemy go
jeszcze pod koniec tego rozdziału:
@WebServlet(name = UploadServlet , urlPatterns = { /UploadServlet })
@MultipartConfig(location= /folder , fileSizeThreshold=1024*1024,
maxFileSize=1024*1024*3, maxRequestSize=1024*1024*3*3)
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
for (Part part : request.getParts()) {
String filename = ;
for (String s: part.getHeader( content-disposition ).split( ; )) {
if (s.trim().startsWith( filename )) {
filename = s.split( = )[1].replaceAll( \ , );
347
Poleć książkęKup książkęJavaServer Faces 2.2. Mistrzowskie programowanie
}
}
part.write(filename);
}
}
}
Pobieżna analiza kodu źródłowego klasy FacesServlet implementacji JSF 2.2 pozwala zorientować
się, że została ona opatrzona adnotacją @MultipartConfig; zrobiono to właśnie po to, by mogła ona
obsługiwać typ danych używany w przypadku przesyłania plików na serwer.
Jeśli nie znasz sposobu przesyłania plików na serwer opisanego w specyfikacji Servlet 3.0, to wię-
cej informacji na jego temat możesz znaleźć w poradniku na stronie http://docs.oracle.com/
javaee/6/tutorial/doc/glrbb.html.
Po stronie klienta do przesyłania plików na serwer można użyć znacznika form oraz ele-
mentu HTML5 input typu file:
form action= UploadServlet enctype= multipart/form-data method= POST
input type= file name= file
input type= Submit value= Prześlij plik
/form
W rzeczywistości komponent JSF 2.2 do przesyłu plików jest jedynie pewnego rodzaju opa-
kowaniem dla powyższego kodu.
Prosty przykład przesyłania plików
z wykorzystaniem możliwości JSF 2.2
W tym punkcie rozdziału przedstawione zostaną podstawowe czynności umożliwiające napisanie
w JSF 2.2 aplikacji pozwalającej na przesyłanie plików na serwer. Choć aplikacja ta jest bardzo
prosta, stanowi ona podstawę dla kolejnych przykładów przedstawionych w tym rozdziale.
W celu zaimplementowania mechanizmu przesyłania plików na serwer po stronie klienta na-
leży wykonać następujące czynności:
1. W znaczniku h:form trzeba określić typ kodowania multipart/form-data, który
pozwoli przeglądarce na wygenerowanie odpowiedniego żądania HTTP POST.
Oto właściwa postać tego znacznika:
h:form id= uploadForm enctype= multipart/form-data
2. Należy skonfigurować znacznik h:inputFile zgodnie z wymaganiami aplikacji.
W tym przykładzie zostanie użyty następujący kod:
h:inputFile id= fileToUpload required= true
requiredMessage= Nie wybrano żadnego pliku...
value= #{uploadBean.file} /
348
Poleć książkęKup książkęRozdział 8. • JSF 2.2 — HTML5 i przesyłanie plików na serwer
3. Trzeba dodać przycisk (lub odnośnik), którego kliknięcie rozpocznie proces
przesyłu pliku, na przykład taki:
h:commandButton value= Prześlij plik action= #{uploadBean.upload()} /
Ewentualnie do formularza można także dodać jakieś znaczniki służące do obsługi komuni-
katów. Przykład formularza z takimi znacznikami został przedstawiony poniżej:
h:messages globalOnly= true showDetail= false
showSummary= true style= color:red /
h:form id= uploadFormId enctype= multipart/form-data
h:inputFile id= fileToUpload required= true
requiredMessage= Nie wybrano żadnego pliku...
value= #{uploadBean.file} /
h:commandButton value= Prześlij plik action= #{uploadBean.upload()} /
h:message showDetail= false showSummary= true
for= fileToUpload style= color:red /
/h:form
Poniższa lista opisuje natomiast czynności do zrealizowania w kodzie wykonywanym na ser-
werze:
1. Zazwyczaj wartością atrybutu value znacznika h:inputFile jest wyrażenie EL
o postaci #{komponent_przesylu.obiekt_part}. Jeśli zmienimy komponent_przesylu
na uploadBean, a obiekt_part na file, to uzyskamy wyrażenie o postaci
#{uploadBean.file}. Obiekt file służy do przechowywania danych przesyłanego
pliku w komponencie UploadBean jako instancji typu javax.servlet.http.Part.
Jedyne, co musimy w tym celu zrobić, to zdefiniować właściwość file
w komponencie, w dokładnie taki sam sposób, w jaki definiowane są wszystkie
inne właściwości:
import javax.servlet.http.Part;
...
private Part file;
...
public Part getFile() {
return file;
}
public void setFile(Part file) {
this.file = file;
}
...
Przesłane dane można odczytać, używając metody getInputStream interfejsu Part.
2. Po kliknięciu przycisku Prześlij plik zostanie wywołana metoda upload().
W momencie wywoływania tej metody cała zawartość przesłanego pliku będzie
już zapisana w obiekcie file, dzięki czemu będzie ją można pobrać w formie
349
Poleć książkęKup książkęJavaServer Faces 2.2. Mistrzowskie programowanie
strumienia (używając w tym celu metody getInputStream) i odpowiednio
przetworzyć. Na przykład można użyć obiektu klasy Scanner, by przetworzyć
przesłane dane na łańcuchy znaków, jak pokazano w poniższym przykładzie:
public void upload() {
try {
if (file != null) {
Scanner scanner = new Scanner(file.getInputStream(), UTF-8 ).
useDelimiter( \\A );
fileInString = scanner.hasNext() ? scanner.next() : ;
logger.info(fileInString);
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage( Udało się pomyślnie przesłać plik na serwer! ));
}
} catch (IOException | NoSuchElementException e) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage( Nie udało się przesłać pliku! ));
}
}
Kompletna wersja tego rozwiązania jest dostępna w kodach dołączonych do książki jako apli-
kacja przyklad_8_03. W jej przypadku przesłane dane są przekształcane na łańcuchy znaków
i wyświetlane w dzienniku, testując ją, warto zatem przesyłać zwyczajne pliki tekstowe za-
wierające czytelne informacje.
Stosowanie wielu elementów h:inputFile
Jeśli zastanawiasz się, czy w tym samym znaczniku h:form można umieścić więcej niż jeden
znacznik h:inputFile , to powinieneś wiedzieć, że odpowiedź na to pytanie jest twierdząca.
Należy przy tym nadać każdemu znacznikowi h:inputFile unikalny identyfikator i skoja-
rzyć go z unikalną instancją typu Part. Aby zastosować dwa znaczniki h:inputFile , zmie-
nimy formularz h:form w sposób przedstawiony poniżej (analogicznie można rozszerzyć
formularz, tak by zawierał trzy, cztery znaczniki h:inputFile czy też jeszcze więcej):
h:form id= uploadFormId enctype= multipart/form-data
h:inputFile id= fileToUpload_1 required= true
requiredMessage= Nie wybrano żadnego pliku...
value= #{uploadBean.file1} /
h:inputFile id= fileToUpload_2 required= true
requiredMessage= Nie wybrano żadnego pliku...
value= #{uploadBean.file2} /
...
h:message showDetail= false showSummary= true
for= fileToUpload_1 style= color:red /
h:message showDetail= false showSummary= true
for= fileToUpload_2 style= color:red /
350
Poleć książkęKup książkęRozdział 8. • JSF 2.2 — HTML5 i przesyłanie plików na serwer
...
h:commandButton value= Prześlij pliki action= #{uploadBean.upload()} /
/h:form
Teraz w kodzie wykonywanym po stronie serwera należy zdefiniować dwie instancje typu Part:
...
private Part file1;
private Part file2;
...
// akcesory i mutatory dla pól file1 i file2
...
Obie instancje Part należy obsłużyć w metodzie upload:
...
if (file1 != null) {
Scanner scanner1 = new Scanner(file1.getInputStream(), UTF-8 ).
useDelimiter( \\A );
fileInString1 = scanner1.hasNext() ? scanner1.next() : ;
logger.info(fileInString1);
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage( Udało się pomyślnie przesłać plik 1 na serwer! ));
}
if (file2 != null) {
Scanner scanner2 = new Scanner(file2.getInputStream(), UTF-8 ).
useDelimiter( \\A );
fileInString2 = scanner2.hasNext() ? scanner2.next() : ;
logger.info(fileInString2);
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage( Udało się pomyślnie przesłać plik 2 na serwer! ));
}
...
Kompletna wersja tego rozwiązania jest dostępna w kodach dołączonych do książki jako apli-
kacja przyklad_8_04.
Pobieranie informacji o przesyłanym pliku
W przypadku przesyłania pliku na serwer do najpotrzebniejszych informacji należą: jego nazwa,
rozmiar, typ zawartości oraz wielkość. W JSF informacje te są dostępne zarówno po stronie
klienta, jak i serwera. Przyjrzyjmy się następującemu znacznikowi h:inputFile :
h:form id= formUploadId enctype= multipart/form-data
h:inputFile id= fileToUpload value= #{uploadBean.file}
required= true requiredMessage= Nie wybrano żadnego pliku...
...
/h:inputFile
/h:form
351
Poleć książkęKup książkęJavaServer Faces 2.2. Mistrzowskie programowanie
A teraz przekonasz się, w jaki sposób można pobrać informacje o pliku wybranym do przesła-
nia na serwer.
Po stronie klienta w tym celu należy wykonać następujące czynności:
Nazwę pliku, jego wielkość (wyrażoną w bajtach) oraz typ zawartości można pobrać
przy użyciu poniższego kodu JavaScript:
var file = document.getElementById( formUploadId:fileToUpload ).files[0];
...
alert(file.name);
alert(file.size);
alert(file.type);
Innym rozwiązaniem jest zastosowanie odpowiedniego wyrażenia w kodzie strony
JSF (oczywiście w tym przypadku informacje o pliku będą dostępne dopiero po jego
przesłaniu na serwer):
// identyfikator komponentu, formUploadId:fileToUpload
#{uploadBean.file.name}
// nazwa przesłanego pliku
#{uploadBean.file.submittedFileName}
// rozmiar przesłanego pliku
#{uploadBean.file.size}
// typ zawartości przesłanego pliku
#{uploadBean.file.contentType}
Po stronie serwera informacje o przesłanym pliku można pobrać w poniższy sposób:
Do pobrania nazwy pliku, jego wielkości (wyrażonej w bajtach) oraz typu
zawartości służy kilka metod interfejsu Part; oto przykład ich zastosowania:
...
private Part file;
...
System.out.println( Identyfikator komponentu file: + file.getName());
System.out.println( Typ zawartości: + file.getContentType());
System.out.println( Nazwa przesłanego pliku: + file.getSubmittedFileName());
System.out.println( Wielkość pliku: + file.getSize());
...
Jeśli łańcuch znaków zwracany przez tę metodę reprezentuje całą ścieżkę dostępu do pliku, a nie tylko
jego nazwę, to konieczne będzie jej wyodrębnienie.
Nazwę pliku można także pobrać z nagłówka content-disposition, jak pokazano
w poniższym przykładzie:
352
Poleć książkęKup książkęRozdział 8. • JSF 2.2 — HTML5 i przesyłanie plików na serwer
private String getFileNameFromContentDisposition(Part file) {
for (String content:file.getHeader( content-disposition ).split( ; )) {
if (content.trim().startsWith( filename )) {
return content.substring(content.indexOf( = ) +1).trim().replace( \ , );
}
}
return null;
}
Przykładową postać nagłówka content-disposition przedstawia rysunek 8.1.
Rysunek 8.1. Przykładowa postać nagłówka content-disposition
Powyższe rozwiązanie można bardzo łatwo zrozumieć, jeśli przeanalizujemy żądanie POST
(używając w tym celu dodatku Firebug lub innego wyspecjalizowanego narzędzia dla pro-
gramistów). Na powyższym rysunku został przedstawiony wybrany fragment informacji pre-
zentowanych przez metodę getFileNameFromContentDisposition.
Kompletna wersja tego rozwiązania jest dostępna w kodach dołączonych do książki jako apli-
kacja przyklad_8_05.
Zapis przesłanych danych na dysku
W poprzednich przykładach plik przesłany na serwer był konwertowany na łańcuch znaków
i wyświetlany w konsoli. Jednak zazwyczaj po odebraniu pliku będziemy chcieli zapisać jego
zawartość na serwerze, w określonym katalogu (załóżmy, że będzie to katalog D:\files). Do te-
go celu można skorzystać z klasy FileOutputStream w sposób przedstawiony w poniższym
przykładzie:
try (InputStream inputStream = file.getInputStream();
FileOutputStream outputStream = new FileOutputStream( D: +
File.separator + files + File.separator +
file.getSubmittedFileName())) {
int bytesRead = 0;
final byte[] chunck = new byte[1024];
while ((bytesRead = inputStream.read(chunck)) != -1) {
outputStream.write(chunck, 0, bytesRead);
}
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage( Udało się pomyślnie przesłać plik na serwer! ));
353
Poleć książkęKup książkęJavaServer Faces 2.2. Mistrzowskie programowanie
} catch (IOException e) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage( Nie udało się przesłać pliku! ));
}
Jeśli chcemy wykonywać operacje wejścia-wyjścia z użyciem bufora, to możemy w tym celu skorzystać
z klas BufferedInputStream oraz BufferedOutputStream.
Kompletna wersja tego rozwiązania jest dostępna w kodach dołączonych do książki jako apli-
kacja przyklad_8_06. Jeśli wolisz pobierać nazwę pliku z nagłówka content-disposition, to
takie rozwiązanie zostało zaimplementowane jako aplikacja przyklad_8_07.
Innym rozwiązaniem pozwalającym na zapisywanie zawartości przesyłanych plików na serwe-
rze jest zastosowanie metody Part.write. W takim przypadku położenie zapisywanego pliku nale-
ży określić przy użyciu znacznika multipart-config (https://docs.oracle.com/javaee/7/tutorial/
servlets011.htm). Oprócz położenia można także ustawić maksymalną wielkość pliku, wielkość żą-
dania oraz próg wielkości pliku. Wszystkie te informacje są zapisywane w pliku web.xml. Po-
niżej przedstawiono przykładowy fragment tego pliku:
servlet
servlet-name Faces Servlet /servlet-name
servlet-class javax.faces.webapp.FacesServlet /servlet-class
load-on-startup 1 /load-on-startup
multipart-config
location D:\files /location
max-file-size 1310720 /max-file-size
max-request-size 20971520 /max-request-size
file-size-threshold 50000 /file-size-threshold
/multipart-config
/servlet
Jeśli plik konfiguracyjny nie będzie zawierał określenia miejsca docelowego plików, to zostanie użyta lo-
kalizacja domyślna, którą jest .
W tym przypadku przesłany plik zostanie zapisany we wskazanym miejscu, pod nazwą prze-
kazaną jako argument wywołania metody Part.write:
try {
file.write(file.getSubmittedFileName());
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage( Udało się pomyślnie przesłać plik na serwer! ));
} catch (IOException e) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage( Nie udało się przesłać pliku! ));
}
354
Poleć książkęKup książkęRozdział 8. • JSF 2.2 — HTML5 i przesyłanie plików na serwer
Kompletna wersja tego rozwiązania jest dostępna w kodach dołączonych do książki jako apli-
kacja przyklad_8_08.
Walidator przesyłanych plików
W większości przypadków trzeba będzie w jakiś sposób ograniczać to, jakie pliki użytkownicy
mogą przesyłać na serwer. Zazwyczaj ograniczenia te dotyczą długości nazwy pliku, jego
wielkości lub typu zawartości. Na przykład można zdecydować się na odrzucanie plików, które:
mają nazwy o długości przekraczającej 25 znaków;
nie są obrazami w formatach PNG lub JPG;
przekraczają 1 MB wielkości.
Takie warunki można zaimplementować w formie następującej klasy:
@FacesValidator
public class UploadValidator implements Validator {
private static final Logger logger =
Logger.getLogger(UploadValidator.class.getName());
@Override
public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException {
Part file = (Part) value;
// WERYFIKACJA DŁUGOŚCI NAZWY PLIKU
String name = file.getSubmittedFileName();
logger.log(Level.INFO, WERYFIKACJA NAZWY PLIKU: {0} , name);
if (name.length() == 0) {
FacesMessage message = new FacesMessage( Błąd przesyłania pliku: Nie można
określić nazwy pliku! );
throw new ValidatorException(message);
} else if (name.length() 25) {
FacesMessage message = new FacesMessage( Błąd przesyłania pliku: Nazwa pliku
jest zbyt długa! );
throw new ValidatorException(message);
}
// WERYFIKACJA TYPU ZAWARTOŚCI PLIKU
if ((! image/png .equals(file.getContentType()))
(! image/jpeg .equals(file.getContentType()))) {
FacesMessage message = new FacesMessage( Błąd przesyłania pliku:
Można przesyłać tylko obrazy PNG i JPG! );
throw new ValidatorException(message);
}
355
Poleć książkęKup książkęJavaServer Faces 2.2. Mistrzowskie programowanie
// WERYFIKACJA WIELKOŚCI PLIKU (rozmiar pliku nie może być większy niż 1 MB)
if (file.getSize() 1048576) {
FacesMessage message = new FacesMessage( Błąd przesyłania pliku: Nie można
przesyłać plików większych niż 1 MB! );
throw new ValidatorException(message);
}
}
}
Taki walidator należy jeszcze dodać do znacznika h:inputFile , jak pokazano w poniższym
przykładzie:
h:inputFile id= fileToUpload required= true
requiredMessage= Nie wybrano żadnego pliku...
value= #{uploadBean.file}
f:validator validatorId= uploadValidator /
/h:inputFile
Teraz na serwerze będzie można zapisywać wyłącznie pliki spełniające podane kryteria.
W przypadku odrzucenia pliku na stronie zostanie wyświetlony komunikat informujący o zbyt
długiej nazwie, przekroczeniu limitu wielkości lub nieodpowiednim typie pliku.
Kompletna wersja tego rozwiązania jest dostępna w kodach dołączonych do książki jako apli-
kacja przyklad_8_09.
Przesyłanie plików z użyciem AJAX-a
Mechanizm przesyłania plików zaimplementowany w JSF pozwala na wykorzystanie możliwości
technologii AJAX poprzez połączenie znaczników h:inputFile i f:ajax bądź też znaczni-
ków h:commandButton (do inicjalizacji przesyłu) i f:ajax . W pierwszym z tych dwóch
przypadków formularz do przesyłu plików zazwyczaj przyjmuje następującą postać:
h:form enctype= multipart/form-data
h:inputFile id= fileToUpload value= #{uploadBean.file} required= true
requiredMessage= Nie wybrano żadnego pliku...
!-- f:ajax listener= #{uploadBean.upload()} render= @all /
w JSF 2.2.0 należy użyć @all --
f:ajax listener= #{uploadBean.upload()} render= fileToUpload /
!-- działa w JSF 2.2.5 --
/h:inputFile
h:message showDetail= false showSummary= true for= fileToUpload
style= color:red /
/h:form
Atrybut render powinien zawierać identyfikatory komponentów, które należy odświeżyć po zakończe-
niu przesyłania pliku. W JSF 2.2.0, w związku z błędem implementacji, konieczne jest użycie słowa klu-
czowego @all zamiast identyfikatorów. W nowszych wersjach JSF błąd ten został już naprawiony i w JSF
2.2.5 wszystko działa zgodnie z oczekiwaniami.
356
Poleć książkęKup książkęRozdział 8. • JSF 2.2 — HTML5 i przesyłanie plików na serwer
Kompletna wersja tego rozwiązania jest dostępna w kodach dołączonych do książki jako apli-
kacja przyklad_8_10.
W drugim z opisanych wcześniej przypadków kombinację znaczników f:ajax oraz h:command
Button można zastosować tak, jak pokazano w poniższym przykładzie:
h:form enctype= multipart/form-data
h:inputFile id= fileToUpload value= #{uploadBean.file} required= true
requiredMessage= Nie wybrano żadnego pliku... /
h:commandButton value= Upload action= #{uploadBean.upload()}
!-- f:ajax execute= fileToUpload render= @all / use @all in JSF 2.2.0 --
f:ajax execute= fileToUpload render= fileToUpload / !-- działa w JSF 2.2.5 --
/h:commandButton
h:message showDetail= false showSummary= true
for= fileToUpload style= color:red /
/h:form
Kompletna wersja tego rozwiązania jest dostępna w kodach dołączonych do książki jako apli-
kacja przyklad_8_11.
Przesyłanie plików z podglądem
Bardzo cenną i wygodną cechą komponentów do przesyłu plików jest możliwość wyświetla-
nia podglądu obrazka przed jego przesłaniem na serwer. Poniższy rysunek 8.2 pokazuje roz-
wiązanie, które zaimplementujemy w tym punkcie rozdziału.
Rysunek 8.2. Przesyłanie plików na serwer z podglądem
Innymi słowy, kiedy użytkownik wybierze obrazek, należy go automatycznie i bezzwłocznie
przesłać na serwer przy użyciu technologii AJAX, zapewniając w ten sposób możliwość wy-
świetlenia go na stronie natychmiast po wybraniu z lokalnego komputera. Żądanie POST wy-
generowane przez AJAX spowoduje zapisanie pliku w obiekcie Part (nadamy mu nazwę file)
na serwerze. Po zakończeniu obsługi żądania AJAX konieczne jest odświeżenie jakiegoś kom-
ponentu pozwalającego na wyświetlanie obrazków, takiego jak h:graphicImage . Komponent
ten odwoła się do serwera, używając żądania GET. Komponent zarządzany odpowiedzialny za
obsługę przesyłanych plików zostanie umieszczony w zasięgu sesji, dlatego też serwlet będzie
357
Poleć książkęKup książkęJavaServer Faces 2.2. Mistrzowskie programowanie
mógł pobrać instancję tego komponentu z sesji i użyć obiektu file reprezentującego przesłany
obrazek. Dzięki temu serwlet będzie mógł przesłać zawartość obrazka (jego bajty), używając
w tym celu strumienia wyjściowego odpowiedzi; będzie też mógł wygenerować jego miniatu-
rę i przesłać ją, aby ograniczyć ilość przesyłanych danych. Następnie, kiedy użytkownik klik-
nie przycisk inicjujący przesyłanie wybranego pliku, należy go jedynie zapisać na dysku.
Tak wygląda idea działania tego rozwiązania. Teraz zajmiemy się jego implementacją i rozbu-
dowaniem o sprawdzanie poprawności, przycisk pozwalający na anulowanie przesyłania oraz
wyświetlaniem informacji o wybranym pliku obok jego podglądu.
W celu zaimplementowania przedstawionego rozwiązania należy wykonać następujące czynności:
1. Przygotować formularz, który będzie automatycznie przesyłał obrazek na serwer
z użyciem AJAX-a:
h:form enctype= multipart/form-data
h:inputFile id= uploadFileId value= #{uploadBean.file} required= true
requiredMessage= Nie wybrano żadnego pliku...
f:ajax render= :previewImgId :imgNameId :uploadMessagesId
listener= #{uploadBean.validateFile()} /
/h:inputFile
/h:form
2. W ramach obsługi żądania AJAX zostanie wywołana metoda validateFile. Ta metoda,
wykonywana na serwerze, potrafi sprawdzić nazwę pliku, jej długość, wielkość
pliku oraz typ jego zawartości. Poniżej przedstawiony został jej kod:
...
private Part file;
...
public void validateFile() {
// WERYFIKACJA DŁUGOŚCI NAZWY PLIKU
String name = file.getSubmittedFileName();
if (name.length() == 0) {
resetFile();
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(
Błąd przesyłania pliku: Nie można określić nazwy pliku! ));
} else if (name.length() 25) {
resetFile();
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(
Błąd przesyłania pliku: Nazwa pliku jest zbyt długa! ));
} else // WERYFIKACJA TYPU ZAWARTOŚCI PLIKU
if ((! image/png .equals(file.getContentType()))
(! image/jpeg .equals(file.getContentType()))) {
resetFile();
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(
Błąd przesyłania pliku: Można przesyłać tylko obrazy PNG i JPG! ));
} else // WERYFIKACJA WIELKOŚCI PLIKU (rozmiar pliku nie może być większy niż 1 MB)
if (file.getSize() 1048576) {
358
Poleć książkęKup książkęRozdział 8. • JSF 2.2 — HTML5 i przesyłanie plików na serwer
resetFile();
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(
Błąd przesyłania pliku: Nie można przesyłać plików większych niż 1 MB! ));
}
}
3. Jeśli warunki określone w metodzie validateFile nie będą spełnione, to zostanie
wywołana metoda resetFile. To bardzo prosta metoda, która odtwarza początkowy
stan obiektu pliku. Co więcej, wywołuje ona także metodę delete, która usuwa
tymczasowe zasoby przydzielone dla przesłanego pliku (w tym także pliki
tymczasowo zapisane na dysku). Metoda resetFile została zdefiniowana w
następujący sposób:
public void resetFile() {
try {
if (file != null) {
file.delete();
}
} catch (IOException ex) {
Logger.getLogger(UploadBean.class.getName()).log(Level.SEVERE, null, ex);
}
file = null;
}
4. Po zakończeniu obsługi żądania AJAX na stronie zostaną odświeżone komponenty
o identyfikatorach: previewImgId, imgNameId oraz uploadMessagesId. Poniższy kod
przedstawia komponenty o identyfikatorach previewImgId oraz imgNameId. W naszej
aplikacji przykładowej identyfikator uploadMessagesId odpowiada komponentowi
h:messages :
...
h:panelGrid columns= 2
h:graphicImage id= previewImgId
value= /PreviewServlet/#{header[ Content-Length ]}
width= #{uploadBean.file.size gt 0 ? 100 : 0}
height= #{uploadBean.file.size gt 0 ? 100 : 0} /
h:outputText id= imgNameId
value= #{uploadBean.file.submittedFileName}
#{empty uploadBean.file.submittedFileName ? : , }
#{uploadBean.file.size} #{uploadBean.file.size gt 0 ? bajtów : } /
/h:panelGrid
...
5. Atrybut value znacznika h:graphicImage odwołuje się do serwletu PreviewServlet.
Serwlet ten udostępnia obrazek, generując jego zawartość przy użyciu strumienia
wyjściowego odpowiedzi, dzięki czemu zapewnia możliwość wyświetlania podglądu
obrazka. Aby uniknąć problemów z mechanizmem pamięci podręcznej, konieczne
jest dodanie do adresu obrazka losowego ciągu znaków (doskonale do tego celu
nada się liczba określająca wielkość zawartości żądania). Dzięki zastosowaniu
359
Poleć książkęKup książkęJavaServer Faces 2.2. Mistrzowskie programowanie
takiego rozwiązania dla każdego żądania będzie wyświetlany odpowiedni, a nie ten
sam, obrazek. Poniżej przedstawiony został fragment implementacji serwletu:
protected void processRequest(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
// w razie potrzeby można dodać bufor, stosując odpowiedni strumień wyjściowy
OutputStream out = response.getOutputStream();
response.setHeader( Expires , Sat, 6 May 1995 12:00:00 GMT );
response.setHeader( Cache-Control , no-store, no-cache, must-revalidate );
response.addHeader( Cache-Control , post-check=0, pre-check=0 );
response.setHeader( Pragma , no-cache );
int nRead;
try {
HttpSession session = request.getSession(false);
if (session.getAttribute( uploadBean ) != null) {
UploadBean uploadBean = (UploadBean) session.getAttribute( uploadBean );
if (uploadBean.getFile() != null) {
try (InputStream inStream = uploadBean.getFile().getInputStream()) {
byte[] data = new byte[1024];
while ((nRead = inStream.read(data, 0, data.length)) != -1) {
out.write(data, 0, nRead);
}
}
}
}
} finally {
out.close();
}
}
6. Powyższy kod zapisze wszystkie bajty przesłanego obrazka w strumieniu wyjściowym
odpowiedzi. W przypadku wyświetlania podglądu przesyłanych obrazków często
jest stosowana technika polegająca na zmniejszaniu obrazka i generowaniu jego
miniaturki, by ograniczyć liczbę przesyłanych bajtów. W Javie obrazek można
przeskalować na wiele różnych sposobów, a jeden z prostszych i szybszych został
przedstawiony poniżej:
protected void processRequest(HttpServletRequest request, HttpServletResponse
response)
throws ServletException, IOException {
OutputStream out = response.getOutputStream();
response.setHeader( Expires , Sat, 6 May 1995 12:00:00 GMT );
response.setHeader( Cache-Control , no-store, no-cache, must-revalidate );
response.addHeader( Cache-Control , post-check=0, pre-check=0 );
360
Poleć książkęKup książkęRozdział 8. • JSF 2.2 — HTML5 i przesyłanie plików na serwer
response.setHeader( Pragma , no-cache );
try {
HttpSession session = request.getSession(false);
if (session.getAttribute( uploadBean ) != null) {
UploadBean uploadBean = (UploadBean) session.getAttribute( uploadBean );
if (uploadBean.getFile() != null) {
BufferedImage image = ImageIO.read(uploadBean.getFile().getInputStream());
BufferedImage resizedImage =
new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = resizedImage.createGraphics();
g.drawImage(image, 0, 0, 100, 100, null);
g.dispose();
ImageIO.write(resizedImage, png , out);
}
}
} finally {
out.close();
}
}
7. Następnie na stronie należy dodać dwa przyciski: Prześlij obraz oraz Anuluj.
Kliknięcie pierwszego z nich rozpoczyna przesyłanie pliku, a kliknięcie drugiego
przerywa operację. Poniższy kod przedstawia oba te przyciski:
h:form
h:commandButton value= Prześlij plik
action= #{uploadBean.saveFileToDisk()} /
h:commandButton value= Anuluj action= #{uploadBean.resetFile()} /
/h:form
8. Po kliknięciu przycisku Prześlij obraz metoda saveFileToDisk zapisze przesłany
plik na dysku; oto jej kod:
public void saveFileToDisk() {
if (file != null) {
// w razie potrzeby można dodać bufor, stosując odpowiedni strumień wyjściowy
try (InputStream inputStream = file.getInputStream();
FileOutputStream outputStream = new FileOutputStream( D: +
File.separator + files + File.separator + file.getSubmittedFileName())) {
int bytesRead;
final byte[] chunck = new byte[1024];
while ((bytesRead = inputStream.read(chunck)) != -1) {
outputStream.write(chunck, 0, bytesRead);
}
resetFile();
361
Poleć książkęKup książkęJavaServer Faces 2.2. Mistrzowskie programowanie
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage( Udało się pomyślnie przesłać plik na serwer! ));
} catch (IOException e) {
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage( Nie udało się przesłać pliku! ));
}
}
}
I gotowe! Kompletna wersja tej aplikacji bez generowania podglądu wybranego obrazka jest
dostępna w kodach dołączonych do książki jako przyklad_8_13. Z kolei aplikacja prezentująca
podgląd obrazka jest dostępna jako przyklad_8_12.
Proces weryfikacji wybranego pliku można usunąć z serwera i przenieść do przeglądarki. Ta-
ka wersja aplikacji także jest dostępna w kodach dołączonych do książki, jako przyklad_8_14.
Kod JavaScript używany do weryfikacji pliku jest całkiem prosty, co pokazano w poniższym
przykładzie:
script type= text/javascript
function validateFile() {
// ![CDATA[
document.getElementById( formSaveId:uploadHiddenId ).value = false;
document.getElementById( validationId ).innerHTML = ;
var file = document.getElementById( formUploadId:fileToUpload ).files[0];
document.getElementById( fileNameId ).innerHTML = b Nazwa pliku: /b +
file.name;
if (file.size 1048576)
fileSize = (Math.round(file.size * 100 / (1048576)) / 100).toString() + MB ;
else
fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + KB ;
document.getElementById( fileSizeId ).innerHTML = b Wielkość pliku: /b +
fileSize;
document.getElementById( fileContentTypeId ).innerHTML = b Typ pliku: /b +
file.type;
// WERYFIKACJA DŁUGOŚCI NAZWY PLIKU
if (file.name.length === 0) {
clearUploadField();
document.getElementById( validationId ).innerHTML =
ul li Błąd przesyłania pliku: Nie można określić nazwy pliku! /
li /ul ;
return false;
}
if (file.name.length 25) {
362
Poleć książkęKup książkęRozdział 8. • JSF 2.2 — HTML5 i przesyłanie plików na serwer
clearUploadField();
document.getElementById( validationId ).innerHTML =
ul li Błąd przesyłania pliku: Nazwa pliku jest zbyt długa! /li /ul ;
return false;
}
// WERYFIKACJA TYPU ZAWARTOŚCI PLIKU
if (file.type !== image/png file.type !== image/jpeg ) {
clearUploadField();
document.getElementById( validationId ).innerHTML = ul li Błąd przesyłania
pliku: Można przesyłać tylko obrazy PNG i JPG! /li /ul ;
return false;
}
// WERYFIKACJA WIELKOŚCI PLIKU (rozmiar pliku nie może być większy niż 1 MB)
if (file.size 1048576) {
clearUploadField();
document.getElementById( validationId ).innerHTML = ul li Błąd przesyłania
pliku: Nie można przesyłać plików większych niż 1 MB! /li /ul ;
return false;
}
document.getElementById( formSaveId:uploadHiddenId ).value = true;
return true;
// ]]
}
function clearUploadField()
{
document.getElementById( previewImgId ).removeAttribute( src );
document.getElementById( imgNameId ).innerHTML = ;
document.getElementById( uploadMessagesId ).innerHTML = ;
var original = document.getElementById( formUploadId:fileToUpload );
var replacement = document.createElement( input );
replacement.type = file ;
replacement.id = original.id;
replacement.name = original.name;
replacement.className = original.className;
replacement.style.cssText = original.style.cssText;
replacement.onchange = original.onchange;
// ...dalsze atrybuty
original.parentNode.replaceChild(replacement, original);
}
/script
363
Poleć książkęKup książkęJavaServer Faces 2.2. Mistrzowskie programowanie
Przesyłanie większej liczby plików
Domyślnie JSF 2.2 nie zapewnia możliwości przesyłania większej liczby plików, jednak taki
rezultat można łatwo uzyskać, wprowadzając kilka modyfikacji. W celu zapewnienia możliwo-
ści przesyłania kilku plików należy się skoncentrować na dwóch wymienionych poniżej za-
gadnieniach:
Zapewnieniu możliwości wyboru większej liczby plików.
Przesłaniu wszystkich wybranych plików na serwer.
Jeśli chodzi o pierwsze z tych zagadnień, to możliwość wyboru większej liczby plików zapewnia
element input języka HTML5 z atrybutem multiple oraz mechanizm atrybutów przekazywa-
nych JSF 2.2. W przypadku zastosowania tego atrybutu i przypisania mu wartości true okno
dialogowe przeglądarki pozwala na zaznaczenie więcej niż jednego pliku. A zatem pierwsze
zagadnienie sprowadza się do wprowadzania minimalnych modyfikacji:
html xmlns= http://www.w3.org/1999/xhtml
xmlns:h= http://xmlns.jcp.org/jsf/html
xmlns:f5= http://xmlns.jcp.org/jsf/passthrough
...
h:form id= uploadFormId enctype= multipart/form-data
h:inputFile id= fileToUpload required= true f5:multiple= multiple
requiredMessage= Nie wybrano żadnego pliku...
value= #{uploadBean.file} /
h:commandButton value= Prześlij action= #{uploadBean.upload()} /
/h:form
Drugi problem jest nieco bardziej złożony, gdyż w przypadku wybrania większej liczby plików
przesłanie każdego kolejnego pliku sprawi, że JSF nadpisze wcześniejszą instancję Part. To zro-
zumiałe zachowanie, ponieważ standardowo używana jest tylko jedna instancja typu Part, a w tym
przypadku potrzebna jest ich kolekcja. Rozwiązanie tego problemu wymaga skoncentrowania się
na mechanizmie wizualizacji komponentu do przesyłania plików. Nosi on nazwę FileRenderer
(i jest rozszerzeniem klasy TextRenderer), a kluczowym elementem naszego problemu jest
przedstawiona poniżej implementacja jego metody decode (w szczególności fragment kodu
wyróżniony pogrubieniem):
@Override
public void decode(FacesContext context, UIComponent component) {
rendererParamsNotNull(context, component);
if (!shouldDecode(component)) {
return;
}
String clientId = decodeBehaviors(context, component);
if (clientId == null) {
clientId = component.getClientId(context);
364
Poleć książkęKup książkęRozdział 8. • JSF 2.2 — HTML5 i przesyłanie plików na serwer
}
assert (clientId != null);
ExternalContext externalContext = context.getExternalContext();
Map String, String
Pobierz darmowy fragment (pdf)