Nyní si konečně vytvoříme první velice jednoduchou aplikaci. Uživatel, který navštíví stránku pouze zadá do vstupního pole své jméno a potvrdí ho, načež se mu zobrazí uvítací zpráva se zadaným jménem. Na příkladu budeme demonstrovat, z čeho se JSF aplikace skládá a základní kroky při vývoji. Programový kód a obsah veškerých souborů k této ukázkové aplikace najdete v příloze A. Snímky obou oken aplikace můžete vidět na konci kapitoly na obrázcích 3.5 a 3.6.
Kód první aplikace je umístěn celý v příloze proto, aby bylo možno utvořit si ucelenou představu o koncepci a architektuře JSF. Ve zbytku textu najdete v kapitolách pouze fragmenty kódu, aby byl čtenář nucen psát si další aplikace sám.
JSF aplikace je z velké části podobná klasickým webovým aplikacím, má však i svá specifika [8]. Měla by se skládat z následujících komponent (souborů):
Klasické JSP soubory.
Množina tzv. managed
beanů, což jsou třídy, které dodržují
konvence JavaBeans modelu[12] a jsou spravovány JSF frameworkem. Jejich konfigurace se
provádí v souboru faces-config.xml
(viz sekce 3.3.2.2 – „Konfigurace backing beanů“). Dle funkcionality, kterou managed beany zajišťují, rozlišujeme
několik druhů. Pro nás budou nejdůležitější tzv. backing beany, jenž
zajišťují funkcionalitu UI komponent na stránce. Mohou například
udržovat informace o jejich stavu, provádět validaci dat, zpracovávat
události atd.[13]
Konfigurační soubor JSF aplikace, ve
kterém se definují pravidla pro navigaci mezi stránkami a konfigurují
managed beany. Jedná se o XML
soubor, který má zpravidla název faces-config.xml
.
Deployment
descriptor, což je také XML soubor, který
musí být přítomen v každé webové aplikaci. Nabízí poměrně široké
možnosti konfigurace, např. nastavení uvítacích stránek, mapování
servletů na různá URL, filtry, kontextové parametry aj. Povinný název
souboru je web.xml
.[14]
Aplikace může dále obsahovat komponenty vytvořené vývojářem. Může se jednat např. o vlastní UI komponenty, vlastní konvertory či validátory atp. Vývoj těchto komponent však přesahuje rozsah práce.
Při tvorbě JSF aplikace musíme postupně vytvořit a naprogramovat všechny výše popsané komponenty. My budeme postupovat následovně:
Tvorba backing beanů.
Konfigurace backing beanů v souboru
faces-config.xml
.
Tvorba JSP stránek za použití tagů JSF.
Definování pravidel pro navigaci mezi stránkami v souboru faces-config.xml
.
Namapování požadavků na instanci FacesServlet
.[15]
Nasazení aplikace na aplikační server.
Všechny tyto kroky si nyní postupně projdeme a dopracujeme se tak k naší první JSF aplikaci. Pro představu uvádím, jak by měla vypadat její finální adresářová struktura.
Backing bean je třída napsána v jazyce Java, která je provázána s komponentami uživatelského rozhraní na stránce. Zodpovědnost za backing beany má osoba, která je znalá v programování v Javě. Dle Java EE specifikací se jedná o application developera. Page author poté jen páruje komponenty uživatelského rozhraní s příslušnými backing beany pomocí speciálního jazyka zvaného Unified Expression Language (EL).[16]
V našem příkladu používáme jeden velice jednoduchý backing bean, třídu Uzivatel
.
Naleznete v ní jeden ze základních prvků JavaBeans modelu, a sice že každá proměnná musí mít svůj
getter nebo setter nebo obojí, podle toho, zda se jedná o proměnnou
určenou pro čtení nebo pro zápis.
Příklad 3.1.
Backing bean
Uzivatel.java
package priklady; public class Uzivatel { private String jmeno; public String getJmeno() { return jmeno; } public void setJmeno(String jmeno) { this.jmeno = jmeno; } }
Aby mohly být backing beany používány v
aplikaci, nestačí je pouze naprogramovat. Je také třeba zadat o nich určité
informace do konfiguračního souboru faces-config.xml
. JSF aplikace potřebuje o
beanu vědět, jak ho pojmenovat, z
jaké třídy se má vytvořit a v jakém rozsahu působnosti ho vytvořit. Rozsah
působnosti (tzv. scope) aplikaci říká,
odkud bude instance dostupná a jak dlouho má existovat. Může nabývat čtyř
hodnot:
application
- přístupnost a životnost v rámci celé
aplikace
session
- přístupnost a životnost v rámci jedné
uživatelské relace
request
- přístupnost a životnost v rámci jediného
requestu
none
- instance takového beanu se nikde neukládají, při každém přístupu je
vytvořena instance nová
V naší aplikaci jsme zvolili rozsah působnosti
session
.
Příklad 3.2. Konfigurace backing beanu
<managed-bean> <managed-bean-name>uzivatel</managed-bean-name> <managed-bean-class>priklady.Uzivatel</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean>
Tag managed-bean
ohraničuje konfiguraci jednoho managed beanu. Tag
managed-bean-name
udává identifikátor, kterým se budu na
bean v celé aplikaci odkazovat,
managed-bean-class
říká z jaké třídy bean vytvořit a managed-bean-scope
definuje rozsah působnosti beanu. V
souboru můžeme také definovat, s jakou hodnotou se mají inicializovat
jednotlivé proměnné. Tuto možnost však v naší aplikaci nevyužíváme.
JSF soubory mohou samozřejmě obsahovat klasické JSP tagy, HTML tagy a
další objekty, které znáte z různých webů, např. klientské skripty. Obsahují
však také některé specifické elementy, které v jiných typech souborů
nenaleznete. V souboru index.jsp
z naší aplikace se nachází několik
základních JSF elementů. Ty rozeznáte na stránce podle toho, že jsou uvedeny
předponou, kterou si page author zvolí
při deklaraci JSF knihoven tagů. Tyto knihovny si nadeklarujeme níže (více
se budeme jednotlivými JSF elementy zabývat v sekci 4.3 – „Použití UI komponent na stránce“).
Soubor index.jsp
si
nyní postupně projdeme a elementy si vysvětlíme, přičemž se nebudeme zabývat
objekty, které nejsou specifické pro JSF, jako např. HTML a JSP tagy. Nejsou
totiž předmětem této práce a předpokládá se zde jejich znalost.
Pokud chceme na stránce používat JSF komponenty, je na ní třeba nadeklarovat dvě knihovny.[17] Ta první má název core tag library a obsahuje 20 tagů. Najdeme zde například tag pro ohraničení veškerých JSF komponent na stránce, dále tagy pro validátory, listenery atp. Tagy z této knihovny nejsou závislé na použité renderovací technologii.[18] Výpis tagů z knihovny core tag library najdete v příloze B.
Druhá knihovna má název HTML component tag library a obsahuje zejména tagy pro vkládání UI komponent na stránku, např. tlačítka, formulářová pole atp. Tyto komponenty jsou závislé na použité renderovací technologii. Výpis všech 25 tagů z knihovny HTML component tag library najdete v příloze C.[19]
Příklad 3.3. Deklarace knihoven JSF tagů
<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%> <%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>
Ve výpise můžete vidět jedinečné URI, pomocí kterých se musíte
na tyto knihovny vždy odkazovat, a také prefixy f
a
h
, pomocí kterých budete uvozovat všechny tagy
příslušné knihovny. Přestože si můžete prefixy zvolit libovolně,
doporučuji držet se zavedené konvence a používat f
a h
.
view
a
form
:Všechny JSF tagy na stránce musí být uzavřeny v tagu
view
, který se nachází v core tag library.
FacesServlet
si poté ze všech tagů
uvnitř view
vytvoří jakýsi model stránky, pomocí
kterého k nim přistupuje. Tento model můžeme znázornit jako
strom jednotlivých komponent.
Tag form
je obdoba stejnojmenného HTML tagu.
Tento tag patří do HTML component tag
library. Veškeré JSF komponenty, které umožňují
uživateli zadat nějaká vstupní data a odeslat je na server musí
být umístěny uvnitř tohoto tagu. Mezi takové komponenty patří
např. inputText
či
commandButton
.
Příklad 3.4. Tagy view
a form
<f:view> <html> ... <body> ... <h:form> <p>Zadejte vaše jméno: <h:inputText value="#{uzivatel.jmeno}"/></p> <h:commandButton action="ok" value="Potvrdit"/> </h:form> </body> </html> </f:view>
outputText
,
inputText
a
commandButton
:Tyto tři tagy, jenž se nacházejí v naší aplikaci, jsou jedny z mnoha tagů, které můžeme použít pro tvorbu uživatelského rozhraní. Všechny tři patří do HTML component tag library.
Tag outputText
vypisuje na obrazovku text, který
je dán atributem value
. Tento atribut přijímá
obyčejný řetězec, ale pomocí jazyka EL do něj můžeme zadat také
název proměnné či metody backing
beanu, ze které řetězec získáme.
Tag inputText
reprezentuje vstupní pole, do
kterého může uživatel zadat textový řetězec. Atributem
value
určujeme hodnotu vstupního pole. V naší
aplikaci tento atribut obsahuje výraz jazyka EL, pomocí kterého
spojujeme hodnotu vstupního pole s hodnotou proměnné
jmeno
třídy Uzivatel
.
Výrazem před tečkou tedy definujeme třídu a výrazem za tečkou
proměnnou. Výraz uzivatel
se zde však neodkazuje
přímo na název třídy, nýbrž na název, který jsme si u
příslušného backing beanu
nastavili v konfiguračním souboru faces-config.xml
(viz
sekce 3.3.2.2 – „Konfigurace backing beanů“).
Pomocí tagu commandButton
umístíme na stránku
tlačítko, které odesílá data z formulářových polí na server.
Atributem value
zde určujeme popisek, který se na
tlačítku objeví, a atributem action
definujeme
akci, kterou stisk tlačítka vyvolá. Opět lze vložit buď prostý
řetězec, jako v naší aplikaci, nebo můžeme volat funkci, která
na základě určité operace vrátí řetězec akce. Tento řetězec se
pak využívá pro definici pravidel pro navigaci mezi stránkami,
která se provádí v konfiguračním souboru faces-config.xml
(definicí pravidel pro navigaci mezi stránkami provedeme v sekci
3.3.2.4 – „Definování pravidel pro navigaci mezi stránkami“).
Příklad 3.5. Tagy outputText
, inputText
a
commandButton
<h1><h:outputText value="První JSF aplikace"/></h1> <h:form> <p>Zadejte vaše jméno: <h:inputText value="#{uzivatel.jmeno}"/></p> <h:commandButton action="ok" value="Potvrdit"/> </h:form>
V druhém souboru naší aplikace vitejte.jsp
se nenachází žádný JSF tag, který
bychom nyní neprobrali. Není proto třeba se tímto souborem zabývat. Snad jen
poznamenám, že atribut value
u tagu outputText
se
odkazuje na tu stejnou proměnnou, do které uživatel na předchozí stránce
vložil text. Hodnotu této proměnné, tedy uživatelské jméno, nyní z této
proměnné načítáme.
Více se jednotlivými tagy budeme zabývat v sekci 4.3 – „Použití UI komponent na stránce“.
Definování pravidel pro navigaci mezi stránkami znamená stanovit pravidla,
podle kterých FacesServlet
jednoznačně pozná, na
kterou stránku přejít po tom, co uživatel klikne na tlačítko či URL odkaz na
aktuální stránce. V JSF se pro stanovení těchto pravidel používá odlišný
způsob, než na jaký jste zvyklí z JSP. Místo psaní klasických URL adres se u
každého tlačítka respektive odkazu stanoví pomocí atributu
action
logický výstup operace. Ten má podobu textového
řetězce a můžeme ho buď zapsat napevno, nebo vygenerovat nějakou metodou.
Stanovení logického výstupu napevno jsme použili v naší aplikaci.
Příklad 3.6. Stanovení logického výstupu operace staticky
<h:commandButton action="ok" value="Potvrdit"/>
Kdybychom chtěli výstup generovat dynamicky, volaná metoda musí vracet typ
Object
či jakéhokoli potomka. Pro určení řetězce
se následně použije metoda toString()
. Běžnou praxí je vracet
rovnou řetězec typu String
. Tuto metodu pak voláme
pomocí EL výrazu. Metoda a její volání ze stránky by mohlo vypadat
následovně.
Příklad 3.7. Stanovení logického výstupu operace dynamicky
public String vystup() { if (objednavka.suma() > 1000) { return "sleva"; } else { return "bezSlevy"; } } <h:commandButton action="#{objednavka.vystup}" value="Potvrdit"/>
Jak naložit s tímto logickým výstupem se
FacesServlet
dozví z konfiguračního souboru
faces-config.xml
.
Tam se stanovují pravidla, pomocí kterých lze jednoznačně určit na jakou
stránku pokračovat. V konfiguračním souboru naší aplikace vypadá stanovení
jediného pravidla následovně:
Příklad 3.8. Definování pravidel pro navigaci mezi stránkami
<navigation-rule> <from-view-id>/index.jsp</from-view-id> <navigation-case> <from-outcome>ok</from-outcome> <to-view-id>/vitejte.jsp</to-view-id> </navigation-case> </navigation-rule>
Tag navigation-rule
ohraničuje pravidla pro navigaci z jedné
stránky. V našem případě ze stránky index.jsp
. Pokud bychom chtěli přidat
pravidla pro další stránku, přidali bychom další tag
navigation-rule
. Tag navigation-case
zase
konfiguruje jeden logický výstup z dané stránky. Tento výstup se nachází v
tagu from-outcome
a následný tag to-view-id
říká,
na jakou stránku v případě daného výstupu pokračovat. Konfigurace více
stránek s více možnými výstupy by mohla vypadat následovně:
Příklad 3.9. Definování pravidel pro navigaci mezi stránkami II
<navigation-rule> <from-view-id>/index.jsp</from-view-id> <navigation-case> <from-outcome>ok</from-outcome> <to-view-id>/vitejte.jsp</to-view-id> </navigation-case> <navigation-case> <from-outcome>chyba</from-outcome> <to-view-id>/chyba.jsp</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>/vitejte.jsp</from-view-id> <navigation-case> <from-outcome>ok</from-outcome> <to-view-id>/katalog.jsp</to-view-id> </navigation-case> </navigation-rule>
Aby jakákoliv JSF aplikace mohla fungovat, musí splňovat jednu základní
podmínku - veškeré požadavky, které na ni směřují, musí jít přes
servlet jménem FacesServlet
.[20] Ten je součástí JSF knihovny umístěné na aplikačním serveru a
nachází se v balíčku javax.faces.webapp
. Instance
tohoto servletu, která je vytvořena webovým kontejnerem při prvním přístupu,
řídí celý životní cyklus a inicializuje veškeré potřebné zdroje. Z pohledu MVC[21] architektury vystupuje FacesServlet
v
roli controlleru.
Z uvedených důvodů je potřeba namapovat
FacesServlet
na určité URL vzory.[23] Mapování servletů se provádí v souboru deployment descriptoru
web.xml
a existují dva
způsoby, jak ho realizovat - tzv. prefix mapping a
extension
mapping.
Základem této metody je namapovat v deployment descriptoru
FacesServlet
na předponu, kterou si
libovolně zvolíme. Tagy pro mapování vypadají následovně:
Příklad 3.10. Mapování FacesServletu
metodou
prefix mapping
<servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>/faces/*</url-pattern> </servlet-mapping>
Tag servlet
definuje základní vlastnosti
servletu. Důležitější je pro nás v tuto chvíli tag
servlet-mapping
, kde je nakonfigurováno
mapování. Nejprve je uvedeno jméno servletu a následně mu
přiřazujeme URL vzor s předponou. Jak vidíte, v našem příkladu
jsme zvolili předponu faces
. To znamená, že
jakákoliv URL adresa ve tvaru http://adresaserveru:port/contextPath/faces/soubor.jsp
[24] povede na FacesServlet
. Jako
konkrétní příklad můžeme uvést adresu, kterou používám na
localhostu pro testování
naší první aplikace: http://localhost:8080/priklad3.1/faces/index.jsp
.
Jelikož je v URL vzoru za příponou faces
hvězdička,
tzn. zástupný znak, mohli bychom soubor index.jsp
nahradit
jakýmkoliv jiným existujícím JSP souborem.
Jak z názvu vyplývá, v případě extension mappingu si nevolíme předponu, nýbrž
příponu, kterou použijeme na mapování. Pokud bychom zůstali u
slůvka faces
, vypadalo by mapování v souboru
web.xml
následovně:
Příklad 3.11. Mapování servletu FacesServlet
metodou extension mapping
<servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.faces</url-pattern> </servlet-mapping>
Jediná změna oproti příkladu 3.10 nastala v
tagu url-pattern
, kde nyní nepoužíváme prefix mapping, nýbrž extension mapping.
Webový kontejner pošle v tomto případě jakýkoliv požadavek na
soubor s příponou .faces na FacesServlet
.
Ten pak zajistí záměnu naší přípony za příponu .jsp a bude
hledat odpovídající soubor. To znamená, že například požadavek
http://localhost:8080/priklad3.1/vitejte.faces
bude automaticky přesměrován na
FacesServlet
a ten následně zamění
příponu a načte soubor vitejte.jsp
.
Nyní, když máme FacesServlet
jedním ze zmíněných
způsobů namapován, máme tři možnosti, jak aplikaci správně spustit:
Prvním a nejjednodušším způsobem je napsat správnou URL adresu do adresního řádku našeho prohlížeče. Jako příklad mohou posloužit URL adresy, které jsem uvedl u obou způsobů mapování. Nevýhodou tohoto řešení je požadavek na uživatele, který musí znát správnou předponu respektive příponu, kterou vývojář zvolil.
Druhá možnost je použít jako uvítací stránku[25] JSP soubor, který bude ve svém těle obsahovat pouze tag
pro přesměrování na správnou URL adresu, která spouští
FacesServlet
. Tento tag by mohl v případě
prefix mappingu vypadat
takto: <jsp:forward page="faces/welcome.jsp"/>
. V
případě extension mappingu zase
takto: <jsp:forward page="index.faces"/>
. Uživatel v
tomto případě nepotřebuje znát předponu respektive příponu. Když
opět použijeme náš příklad na localhostu, tak stačí, aby uživatel zadal http://localhost:8080/priklad3.1/
.
Automaticky se tak načte nastavená uvítací stránka, které si však
ani nevšimneme, neboť nás ihned přesměruje na JSF stránku přes
FacesServlet
.
Poslední možností je nakonfigurovat v deployment deskriptoru URL uvítací stránky již s
předponou. Konfigurace v souboru web.xml
by vypadala
následovně:
Příklad 3.12. Nastavení uvítací stránky
<welcome-file-list> <welcome-file>faces/index.jsp</welcome-file> </welcome-file-list>
Příponu jsem zde nezmínil záměrně, neboť toto řešení v kombinaci s
extension mappingem
nepřichází v úvahu. Uvítací stránka totiž musí být fyzicky
existující soubor, což např. index.faces
není.
Je to pouze pseudonázev, se kterým si umí poradit
FacesServlet
, ale webový kontejner by
takový soubor nedokázal identifikovat.
Při aplikaci tohoto řešení by uživatel opět nemusel znát předponu, a ani by nedocházelo k přesměrování. Tento způsob je použit také v naší JSF aplikaci.
Možná se nyní ptáte, co by se stalo, kdyby se uživatel pokusil
zadat do prohlížeče URL, které neodpovídá žádnému vzoru a požadavek
by tak neprošel přes FacesServlet
. V takovém
případě by nebylo zajištěno korektní započetí životního cyklu
aplikace a server by vyhodil výjimku
RuntimeException
s chybovým hlášením, že
FacesContext
nebyl nalezen. Instance
třídy FacesContext
je inicializovaná při
každém požadavku a uchovává veškeré potřebné informace, které se k
němu vztahují (pro více informací o třídě
FacesContext
viz sekci 7 – „Životní cyklus JSF aplikace“ nebo [6]).
Deployment je poslední krok, který musíme provést předtím, než naší aplikaci konečně spustíme. Na server je třeba nasadit soubor s příponou .war, což je klasický Java archiv se strukturou specifickou pro Java EE webové aplikace.[26] Přesný postup nasazení se však může u různých aplikačních serveru lišit. Z toho důvodů, a také proto, že se nejedná o nijak složitý proces, vás odkážu na dokumentaci k serveru, který používáte. Pokud však používáte aplikační server GlassFish, naleznete jednoduchý postup v příloze E.
Když se vám povede aplikaci správně nasadit, stačí do prohlížeče zadat URL
adresu a měla by se spustit. V případě naší aplikace nasazené na GlassFish
byste po zadání adresy http://localhost:8080/priklad3.1/
[27] měli vidět přibližně to, co vidíte na obrázku 3.5.
Po zadání jména a potvrzení byste pak měli vidět to, co vidíte na obrázku 3.6.
[12] Pro více informací o modelu JavaBeans viz http://java.sun.com/docs/books/tutorial/javabeans.
[13] Je důležité neplést si managed beany, které se nacházejí ve webové vrstvě, s enterprise Java beany z business vrstvy. Ty totiž nezajišťují funkcionalitu webových stránek, nýbrž stěžejní funkcionalitu celé enterprise aplikace a navíc se nacházejí v business vrstvě. Pro více informací o EJB viz [8].
[15] FacesServlet je třída, jejíž instance řídí celý životní cyklus
JSF aplikace. Přes tuto instanci musí projít veškeré requesty.
Více se touto třídou budeme zabývat v sekci 3.3.2.5 – „Namapování požadavků na instanci
FacesServlet
“.
[17] Problematiku knihoven tagů, byste měli znát z JSP technologie. Pro více informací o knihovnách tagů viz např. [1].
[18] Renderovací technologie představuje souhrn pravidel, která determinují, jak mají být JSF tagy prezentovány na koncovém zobrazovacím zařízení. Jako příklad můžeme uvést tag commandButton, který představuje tlačítko a standardní renderovací třídy JSF ho na koncovém zařízení prezentují pomocí HTML tagu input třeba takto: <input type="submit" value="Potvrdit"/>. Více se renderováním UI komponent budeme zabývat v sekci 4.2 – „Renderování UI komponent“.
[21] V tomto kontextu mám na mysli MVC architekturu z pohledu izolované webové aplikace. Pokud bychom se na enterprise aplikaci o čtyřech vrstvách podívali z perspektivy, MVC model by vypadal jinak.
[22] View JSF aplikace je tvořeno tzv. RenderKity, což je sada tříd, které mají na starost renderování UI komponent na výstupní zařízení. Více se budeme renderováním UI komponent zabývat v sekci 4.2 – „Renderování UI komponent“.
[23] URL vzor je řetězec složený ze znaků, které mohou být obsaženy v URL, a zástupných znaků (např. *), které představují libovolný znak. Takovému vzoru pak odpovídá určitá množina URL adres. Pro více informací o URL vzorech viz [8].
[24] Context path je adresář, který identifikuje aplikaci v rámci aplikačního serveru. Pro více informací o context path viz [8].
[25] Uvítací stránka aplikace se nastavuje v souboru web.xml pomocí tagu welcome-file-list (viz 3.3).
[27] Tvar URL adresy záleží na tom, jakým způsobem jste namapovali FacesServlet.