Java Server Faces: Porovnání verzí

Z FI WIKI
Přejít na: navigace, hledání
(Controller Vrstva = Backing Bean)
m
 
(Není zobrazeno 25 mezilehlých verzí od 16 dalších uživatelů.)
Řádka 5: Řádka 5:
  
 
== Úvod ==
 
== Úvod ==
Java Server Faces je rámec postavený přímo na Servlet API. Může používat JSP, ale nemusí.
+
Java Server Faces je komponentově orientovaný webový rámec (pull-based), je tedy jiného typu než klasický přístup [[Struts]] či [[Stripes]]. V klasickém pojetí je stránka obsluhována jako celek, je zde velký důraz na usnadnění obsluhy paradigmu HTTP Request-Response. Komponentový přístup JSF deleguje obsluhu stránky jako takové na jednotlivé komponenty, které si svoje místečko na webové stránce obhospodařují samy.  
Pokud používá JSP, jsou k dispozici dvě knihovny JSP značek.
+
  
Stránky jsou skládány z komponent. Jedna komponenta může mít více různých ''rendererů'',
+
JSF, stejně jako většina webových frameworků, je postaveno na Servlet API a [[JSP]]. [[JSP]] jako view vrstva může být ovšem nahrazena prakticky čímkoliv. Pro [[JSP]] jsou k dispozici dvě knihovny značek (taglib).
např. pro HTML, pro XHTML, pro WAP/WML, nebo i různé varianty v rámci HTML.
+
Standardně je k dispozici základní sada komponent a renderery do HTML.  
+
  
Např. komponenta <code>UICommand</code> se může zobrazit
+
Jednou z nejčastěji kritizovaných vlastností JSF je obecnost, se kterou lze v JSF vyměnit prakticky kteroukoliv podčást za jinou. Ve spojení s jednoduchostí použití (o což se JSF snaží) je pod povrchem JSF schováno hodně hodně hodně kódu, který je těžké pochopit a při chybě těžké opravit.
* jako hyperlink pomocí značky <code><h:commandLink></code>
+
* jako tlačítko pomocí značky <code><h:commandButton></code>
+
 
+
Nevýhodou implementací JSF je, že renderery pro HTML si často vypomáhají JavaScriptem,
+
a aplikace tak nebudou fungovat v prohlížečích, které mají JavaScript vypnutý.
+
 
+
Implementace JSF má předpřipravené komponenty pro běžné prvky - hyperlinky, tlačítka,
+
editovatelná pole, výběry ze seznamů, zobrazení seznamu chyb atd.
+
  
 
Specifikaci vytvořili:
 
Specifikaci vytvořili:
Řádka 26: Řádka 15:
 
* 1.1 - Craig McClanahan, Ed Burns, Roger Kitain
 
* 1.1 - Craig McClanahan, Ed Burns, Roger Kitain
 
* 1.2 - Ed Burns, Roger Kitain
 
* 1.2 - Ed Burns, Roger Kitain
* 2.0 (zatím plenky) - Ed Burns, Roger Kitain
+
* 2.0 - Ed Burns, Roger Kitain
 +
* 2.1 - Ed Burns, Roger Kitain (Aktuální verze, jen drobné úpravy oproti 2.0)
  
=== Výhody ===
+
=== Výhody (proč ano) ===
  
* tzv. "standard", tj. je součástí JavaEE
+
* tzv. "standard", tj. je součástí Java EE
 
* podpora od výrobců vývojových nástrojů
 
* podpora od výrobců vývojových nástrojů
 +
* efektivní při základním použití
 +
* dostatek dokumentace v rámci komunity
 +
* používané <==> vyžadováno sw společnostmi
  
<a href="http://web.skkguz.edu/board/8198615.html">Skabrewno</a> [url=http://web.skgwu.edu/board/8198615.html]Skabrwino[/url] http://web.skgkw.edu/board/8198615.html
+
 
 +
=== Nevýhody (proč ne) ===
 +
* je nutné doplnit o dva další frameworky (Seam,Facelets) aby bylo použitelné
 +
* komplikované až těžkopádné při snaze využít specialitek JSF
 +
* renderery si musí vypomáhat JavaScriptem
 +
* navigace je založena jen na POST
 +
 
 +
[[Image:Jsfkoule.gif]]
  
 
== Popis, jak to funguje ==
 
== Popis, jak to funguje ==
 +
Pro JSF existuje jeden obslužný servlet FacesServlet, který zajišťuje, že všechny komponenty projdou definovaným životním cyklem během života HTTP požadavku. Pro jednoduchý náhled můžeme dále uvažovat již jen o životním cyklu a o komponentách.
 +
 +
 
=== MVC ===
 
=== MVC ===
JSF jsou silně založeny na konceptu MVC, i když se jedná pouze o prezentační vrstvu třívrstvé architektury.  
+
JSF jsou silně založeny na konceptu [[MVC]], i když se jedná pouze o prezentační vrstvu třívrstvé architektury.  
 
* '''Model''' vrstva by měla být tvořena ideálně pomocí java beanů nesoucí data. V JEE 5 aplikaci se jedná např. o entitní beany.
 
* '''Model''' vrstva by měla být tvořena ideálně pomocí java beanů nesoucí data. V JEE 5 aplikaci se jedná např. o entitní beany.
* '''View''' vrstva používá RenderKit a JSP stránky. V JSP stránkách se vytvoří strom komponent z dvou knihoven JSF (Core a HTML RenderKit) a případných dalších komponenty 3. stran. Strom komponent je provázán na Model a Controller pomocí EL (Expression Language).
+
* '''View''' vrstva používá RenderKit a JSP stránky. V JSP stránkách se vytvoří strom komponent z dvou knihoven JSF (Core a HTML RenderKit) a případných dalších komponent 3. stran. Strom komponent je provázán na Model a Controller pomocí EL (Expression Language). Rámec Facelets řeší převážně tuto vrstvu.
* '''Controller''' vrstva je tvořena pomocí tzv. Backing (Managed, jak se vám líbí) beans, což jsou obyčejné JavaBeans, které obstarávají přístup k Modelu a mají za úkol řídit běh aplikace. Tyto beany lze nahradit pomocí EJB 3.0, nicméně toto řešení přináší smíchání prezentační a aplikační vrstvy třívrstvé aplikace.
+
* '''Controller''' vrstva je tvořena pomocí tzv. Backing (Managed, jak se vám líbí) beans, což jsou obyčejné JavaBeans, které obstarávají přístup k Modelu a mají za úkol řídit běh aplikace. Tyto beany lze nahradit pomocí EJB 3.0, nicméně toto řešení přináší ''smíchání prezentační a aplikační vrstvy třívrstvé aplikace!'' Rámec Seam řeší především tuto vrstvu.
  
 
==== Model Vrstva ====
 
==== Model Vrstva ====
Řádka 48: Řádka 51:
 
* '''SelectItem''' s potomkem SelectItemGroup slouží jako model pro UISelectMany a UISelectOne komponentky.
 
* '''SelectItem''' s potomkem SelectItemGroup slouží jako model pro UISelectMany a UISelectOne komponentky.
  
<a href="http://web.skkguz.edu/board/8198615.html">Skabrewno</a> [url=http://web.skgwu.edu/board/8198615.html]Skabrwino[/url] http://web.skgkw.edu/board/8198615.html
+
==== Controller Vrstva = Backing Bean ====
 +
Backing bean, jak bylo již uvedeno, je obvykle normální JavaBean. Tento bean zpracovává události, řídí běh prezentační vrstvy a interaguje s business vrstvou aplikace.  
 +
 
 +
* Musí obsahovat bezparametrický konstruktor, aby jej mohlo JSF dynamicky za běhu instanciovat.
 +
* Většinou obsahuje Gettery/Settery pro přístup k Modelu.
 +
* Obsahuje validační metody.
 +
* Obsahuje metody pro zpracování událostí.
 +
* Protože se jedná o Controller obsahuje bezparametrické metody řídící běh aplikace, které vždy vrací akci (String) pro navigaci.
 +
 
 +
<java>
 +
public class IndexManagedBean {
 +
    private String zvire;
 +
 
 +
    public String getZvire() {
 +
        return zvire;
 +
    }
 +
 
 +
    public void setZvire(String zvire) {
 +
        this.zvire = zvire;
 +
    }
 +
 
 +
    public String processSubmit(){
 +
        System.out.println("Zvire je: " + zvire);
 +
        return "OK";
 +
    }
 +
}
 +
</java>
 +
 
 +
V deklarativní konfiguraci beanu v faces-config.xml můžeme nastavit scope beanu a dokonce i fiktivní atributy, které v definici třídy neexistují:
 +
<xml>
 +
    <managed-bean>
 +
        <managed-bean-name>indexBean</managed-bean-name>
 +
        <managed-bean-class>jsf.backingbean.IndexManagedBean</managed-bean-class>
 +
        <managed-bean-scope>session</managed-bean-scope>
 +
        <managed-property>
 +
            <property-name>nonExistingProperty</property-name>
 +
            <property-class>long</property-class>
 +
            <value>0</value>
 +
        </managed-property>
 +
    </managed-bean>
 +
</xml>
  
 
==== View vrstva ====
 
==== View vrstva ====
View se skládá z UI komponent a JSP stránek, kde jsou jednotlivé komponenty seskládány do stromu, který je přístupný pomocí FacesContext.getCurrentInstance().getViewRoot(). Nicméně lze místo JSP použít i jiné technologie, které je ovšem třeba zvlášť deklarovat.
+
View se skládá z UI komponent a [[JSP]] stránek, kde jsou jednotlivé komponenty seskládány do stromu, který je přístupný pomocí FacesContext.getCurrentInstance().getViewRoot(). Nicméně lze místo JSP použít i jiné technologie, které je ovšem třeba zvlášť deklarovat.
  
 
===== User Interface Components =====
 
===== User Interface Components =====
JSF přináší základní sadů komponentek, které by měly pokrývat většinu potřeb formulářových aplikací.
+
JSF přináší základní sadu komponentek, které by měly pokrývat většinu potřeb formulářových aplikací.
 
* Komponenta jako taková je java třída rozšiřují UIComponent.  
 
* Komponenta jako taková je java třída rozšiřují UIComponent.  
* K ní by měl existovat Renderer, který danou komponentu vykresluje. Každý Renderer vždy patří do nějakého RenderKitu. Lze ovšem i udělat vyjímky, kdy se vykresluje sám potomek UIComponent bez Rendereru. Jedna komponentka může mít dokonce více rendererů (např. UICommand má HTMLCommandLink a HTMLCommandButton).
+
* K ní by měl existovat Renderer, který danou komponentu vykresluje. Každý Renderer vždy patří do nějakého RenderKitu. Lze ovšem i udělat výjimky, kdy se vykresluje sám potomek UIComponent bez Rendereru. Jedna komponentka může mít dokonce více rendererů (např. UICommand má HTMLCommandLink a HTMLCommandButton).
 
* Aby se komponenta dala použít v JSP, jsou v JSF vytvořeny dva základní tagliby: JSF Core (prefix f:) a JSF HTML (prefix h:). Ty obsahují Tagy pro jednotlivé komponenty.
 
* Aby se komponenta dala použít v JSP, jsou v JSF vytvořeny dva základní tagliby: JSF Core (prefix f:) a JSF HTML (prefix h:). Ty obsahují Tagy pro jednotlivé komponenty.
  
 
===== JSP =====
 
===== JSP =====
V rámci JSP se vkládají jednotlivé komponentky na stránku.  
+
V rámci [[JSP]] se vkládají jednotlivé komponentky na stránku.  
 
Všechny komponentky na stránce musí být umístěny do <f:view> tagu.
 
Všechny komponentky na stránce musí být umístěny do <f:view> tagu.
JSF HTML obsahuje všechny důležité komponentky pro formulářové aplikace, které jsou většinou poděděny z UIInput a UIOutput. Formulářové komponentky musí být uvnitř <f:form> tagu.
+
JSF HTML obsahuje všechny důležité komponentky pro formulářové aplikace, které jsou většinou poděděny z UIInput a UIOutput. Formulářové komponentky musí být uvnitř <h:form> tagu.
  
 
Existují dva používané způsoby, jak používat komponentky v aplikaci.
 
Existují dva používané způsoby, jak používat komponentky v aplikaci.
 
* Komponentka je pouze na stránce a pomocí EL se váže pouze Model:
 
* Komponentka je pouze na stránce a pomocí EL se váže pouze Model:
<java>
+
<xml>
    private String ter;
+
  <h:form>
     public void setter(String ter){this.ter = ter;}
+
     <h:inputText value="#{indexBean.zvire}" /><br />
     public String getter(String ter){this.ter = ter;}
+
     <h:inputText value="#{indexBean.nonExistingProperty}" /><br />
</java>
+
    <h:commandButton action="#{indexBean.processSubmit}" />
<xml>Ter: <h:inputText value="#{mujBackingBean.ter}" /></xml>
+
  </h:form>
 +
</xml>
 
* Komponentka je v Backing beanu a referencuje se z JSP:
 
* Komponentka je v Backing beanu a referencuje se z JSP:
 
<java>
 
<java>
 
     private HTMLInputText inputText = new HTMLInputText();
 
     private HTMLInputText inputText = new HTMLInputText();
     public void setInputText(HTMLInputText inputText){this.inputText = inputText;}
+
     public void setInputText(HTMLInputText inputText){
     public String getInputText(HTMLInputText inputText){this.inputText = inputText;}
+
        this.inputText = inputText;
     private String ter;
+
    }
    public void setter(String ter){this.ter = ter;}
+
     public HTMLInputText getInputText(){
    public String getter(String ter){this.ter = ter;}
+
        return this.inputText;
 +
     }
 
</java>
 
</java>
<xml>Ter: <h:inputText value="#{mujBackingBean.ter}" binding="#{mujBackingBean.inputText}/></xml>
+
<xml><h:inputText binding="#{mujBackingBean.inputText}"/></xml>
  
 
=== Konfigurace JSF aplikace ===
 
=== Konfigurace JSF aplikace ===
Řádka 98: Řádka 143:
 
     </servlet-mapping>  
 
     </servlet-mapping>  
 
</xml>
 
</xml>
Takže když příjde Request, webový kontejner spustí FacesServlet, který již sám řídí běh aplikace. Zde je vidno, že je možné používat zároveň JSF s dalšími technologiemi, protože JSF obsluhuje jen ta URL, na které je servlet namapován (v našem případě *.jsf, ale obecně to může být například /javaserverfaces/* či *.tatoStrankaJeObsluhovanaPomociJavaServerFacesHehe ;)).
+
Takže když příjde Request, webový kontejner spustí FacesServlet, který již sám řídí běh aplikace.
 
+
  
 
Celá JSF aplikace se konfiguruje pomocí souboru <code>/WEB-INF/faces-config.xml</code>.
 
Celá JSF aplikace se konfiguruje pomocí souboru <code>/WEB-INF/faces-config.xml</code>.
Řádka 126: Řádka 170:
 
</xml>
 
</xml>
  
Ve verzi JavaEE 5.0 (~JSF 1.2) byl sjednocen JSTL EL a JSF EL do jednoho Unified EL jazyka:
+
Ve verzi [[Java EE]] 5.0 (~JSF 1.2) byl sjednocen [[JSP|JSTL EL]] a JSF EL do jednoho Unified EL jazyka:
 
* výrazy <code>${neco}</code> označují výrazy pro čtení  
 
* výrazy <code>${neco}</code> označují výrazy pro čtení  
 
* výrazy <code>#{neco}</code> označují výrazy, které umožňují i zápis, volání metod a odložené vyhodnocování výrazů
 
* výrazy <code>#{neco}</code> označují výrazy, které umožňují i zápis, volání metod a odložené vyhodnocování výrazů
  
K výše uvedenému managed JavaBeanu můžete tedy přistupovat např. pomocí
+
K výše uvedenému managed JavaBeanu můžete tedy přistupovat pomocí #{UserNumberBean}
 
  <xml>
 
  <xml>
 
  <%@ page contentType="text/html; charset=utf-8" %>
 
  <%@ page contentType="text/html; charset=utf-8" %>
Řádka 160: Řádka 204:
 
     <navigation-case>
 
     <navigation-case>
 
       <from-outcome>dobre</from-outcome>
 
       <from-outcome>dobre</from-outcome>
       <to-view-id>/dobre.jsp</to-view-id>
+
       <to-view-id>/dalsiStranka.jsp</to-view-id>
 
     </navigation-case>
 
     </navigation-case>
 
     <navigation-case>
 
     <navigation-case>
 
       <from-outcome>spatne</from-outcome>
 
       <from-outcome>spatne</from-outcome>
 
       <to-view-id>/spatne.jsp</to-view-id>
 
       <to-view-id>/spatne.jsp</to-view-id>
 +
    </navigation-case>
 +
    <navigation-case>
 +
      <from-outcome>dalsi</from-outcome>
 +
      <to-view-id>/dalsiStranka.jsp</to-view-id>
 
     </navigation-case>
 
     </navigation-case>
 
  </navigation-rule>
 
  </navigation-rule>
 
  </xml>
 
  </xml>
  
Ve stránce pak lze specifikovat, že výstup z akce poskytuje určité metoda:
+
Ve stránce pak lze specifikovat přechod přímo:
 
+
<xml><h:commandButton id="submit-dalsi" action="dalsi" value="Jdi dal" /></xml>
<xml><h:commandButton id="submit" action="#{mujManagedBean.jakDopadlo}" value="Submit" /></xml>
+
 
+
a metoda musí vracet jeden z očekávaných řetězců:
+
  
 +
či typicky rozhodnout v kódu:
 
<java>
 
<java>
 
     public String jakDopadlo() {
 
     public String jakDopadlo() {
Řádka 181: Řádka 227:
 
     }
 
     }
 
</java>
 
</java>
 +
<xml><h:commandButton id="submit" action="#{mujManagedBean.jakDopadlo}" value="Proved akci" /></xml>
 +
 +
 +
=== Lokalizace ===
 +
# JSF podporují lokalizaci pomocí <f:loadBundle /> tagu v rámci JSP
 +
# a pomocí deklarace v faces-config.xml, kde se mimo nastavuje ResourceBundle pro JSF komponentky
 +
 +
V rámci JSP se používá:
 +
<xml>
 +
    <f:loadBundle basename="jsf.resource.Messages" var="bundle"/>
 +
    ...
 +
    <title><h:outputText value="#{bundle.welcome_title}" /></title>
 +
</xml>
 +
nebo
 +
<xml>
 +
    <f:loadBundle basename="js.resource.Messages" var="bundle"/>
 +
    ...
 +
    <title>${bundle.welcome_title}</title>
 +
</xml>
 +
Bohužel <f:loadBundle /> nepodporuje tečkovou notaci kvůli EL, takže nemůžeme použít například <code><h:outputText value="#{bundle.welcomeJSP.title}" /></code>
 +
 +
Deklarace ve faces-config.xml:
 +
<xml>
 +
    <application>
 +
        <locale-config>
 +
            <default-locale>en</default-locale>
 +
            <supported-locale>cs</supported-locale>
 +
        </locale-config>
 +
        <message-bundle>jsf.resource.FacesMessages</message-bundle>
 +
    </application>
 +
</xml>
 +
 +
=== Běh ===
 +
Jak již bylo několikrát zmíněno, běh a životní cyklus řídí <code>FacesServlet</code>. Aby mohlo být předáno řízení FacesServletu, je třeba, aby příchozí request obsahoval URL, na které je FacesServlet namapován.
 +
Pokud je výchozí JSP index.jsp, potom se používá např. tato konstrukce:
 +
<xml>
 +
<!-- web.xml -->
 +
    <welcome-file-list>
 +
        <welcome-file>redirectToJSF.jsp</welcome-file>
 +
    </welcome-file-list>
 +
</xml>
 +
<java>
 +
//redirectToJSF.jsp
 +
    <% response.sendRedirect("index.jsf"); %>
 +
</java>
 +
 +
Nyní již odchytí běh FacesServlet, který použije třídu Lifecycle pro spuštění životního cyklu request-response ([JSF#.C5.BDivotn.C3.AD_cyklus_str.C3.A1nky]]).
  
 
=== Životní cyklus stránky ===
 
=== Životní cyklus stránky ===
Řádka 199: Řádka 292:
 
# Invoke Application; process events - předání řízení Controlleru, tj. backing beanům a zpracuje možné události
 
# Invoke Application; process events - předání řízení Controlleru, tj. backing beanům a zpracuje možné události
 
# Render Response - vygeneruje strom komponent pomocí daného RenderKitu
 
# Render Response - vygeneruje strom komponent pomocí daného RenderKitu
 +
  
 
=== Validace ===
 
=== Validace ===
Řádka 208: Řádka 302:
 
# nebo je možné vypsat všechny zprávy pomocí <code><h:messages /></code>
 
# nebo je možné vypsat všechny zprávy pomocí <code><h:messages /></code>
  
Je třeba dát pozor na validaci povinnosti. Ta se definuje pomocí atributu required v rámci UIInput. Pokud tato validace neprojde, specifikuje se výstupní hláška pomocí property ''javax.faces.component.UIInput.REQUIRED'' v resource bundlu, který je deklarován ve faces-config.xml.
+
Je třeba dát pozor na validaci povinnosti. Ta se definuje pomocí atributu '''required''' v rámci UIInput. Pokud tato validace neprojde, specifikuje se výstupní hláška pomocí property ''javax.faces.component.UIInput.REQUIRED'' v resource bundlu, který je deklarován ve faces-config.xml.
  
 
Pro validace lze obecně použít dvě cesty.
 
Pro validace lze obecně použít dvě cesty.
Řádka 246: Řádka 340:
 
</xml>
 
</xml>
  
JSF podporuje také vytvořit vlastní validátor. Tvorba ovšem podmiňuje všechny tyto kroky :( :
+
JSF podporuje také vytvořit vlastní validátor. Tvorba ovšem podmiňuje všechny tyto kroky :(, což je možná důvod, že se prakticky nové validátory nepoužívají:
 
* Vytvořit Java třídu implementující rozhraní Validator
 
* Vytvořit Java třídu implementující rozhraní Validator
 
* Implementovat metodu <code>public void validate(FacesContext context, UIComponent component, java.lang.Object value) throws ValidatorException</code>, která při chybné validaci vyhodí vyjímku ValidatorException.
 
* Implementovat metodu <code>public void validate(FacesContext context, UIComponent component, java.lang.Object value) throws ValidatorException</code>, která při chybné validaci vyhodí vyjímku ValidatorException.
Řádka 299: Řádka 393:
 
     </h:inputText>
 
     </h:inputText>
 
</xml>
 
</xml>
 +
  
 
=== Konvertory ===
 
=== Konvertory ===
Řádka 333: Řádka 428:
 
     </h:selectOneRadio>
 
     </h:selectOneRadio>
 
</xml>
 
</xml>
=== Lokalizace ===
 
# JSF podporují lokalizaci a internacionalizaci pomocí <f:loadBundle /> tagu v rámci JSP
 
# a pomocí deklarace v faces-config.xml, kde se mimo nastavuje ResourceBundle pro JSF komponentky
 
 
V rámci JSP se používá:
 
<xml>
 
    <f:loadBundle basename="jsf.resource.Messages" var="bundle"/>
 
    ...
 
    <title><h:outputText value="#{bundle.welcome_title}" /></title>
 
</xml>
 
nebo
 
<xml>
 
    <f:loadBundle basename="js.resource.Messages" var="bundle"/>
 
    ...
 
    <title>${bundle.welcome_title}</title>
 
</xml>
 
Bohužel <f:loadBundle /> nepodporuje tečkovou notaci kvůli EL, takže nemůžeme použít například <code><h:outputText value="#{bundle.welcomeJSP.title}" /></code>
 
 
Deklarace ve faces-config.xml:
 
<xml>
 
    <application>
 
        <locale-config>
 
            <default-locale>en</default-locale>
 
            <supported-locale>cs</supported-locale>
 
        </locale-config>
 
        <message-bundle>jsf.resource.FacesMessages</message-bundle>
 
    </application>
 
</xml>
 
 
=== Běh ===
 
Jak již bylo několikrát zmíněno, běh a životní cyklus řídí <code>FacesServlet</code>. Aby mohlo být předáno řízení FacesServletu, je třeba, aby příchozí request obsahoval URL, na které je FacesServlet namapován.
 
Pokud je výchozí JSP index.jsp, potom se používá např. tato konstrukce:
 
<xml>
 
<!-- web.xml -->
 
    <welcome-file-list>
 
        <welcome-file>redirectToJSF.jsp</welcome-file>
 
    </welcome-file-list>
 
</xml>
 
<java>
 
//redirectToJSF.jsp
 
    <% response.sendRedirect("index.jsf"); %>
 
</java>
 
 
Nyní již odchytí běh FacesServlet, který použije třídu Lifecycle pro spuštění životního cyklu request-response ([JSF#.C5.BDivotn.C3.AD_cyklus_str.C3.A1nky]]).
 
  
 
== Implementace JSF ==
 
== Implementace JSF ==
 
V současné době se používají dvě hlavní implementace JSF.  
 
V současné době se používají dvě hlavní implementace JSF.  
 
* RI (reference implementation) je od Sunu ve verzi 1.2
 
* RI (reference implementation) je od Sunu ve verzi 1.2
* MyFaces od Apache je ve verzi 1.1, chystá se uvolnění 1.2 (zdroje již prošly TCL)
+
* MyFaces od Apache je ve verzi 1.2
  
  
Řádka 400: Řádka 451:
  
 
Od verze 2.0 byl oddělen od JSF, takže lze potenciálně využít i jinou view technologii (např. GWT, Google Web Toolkit).
 
Od verze 2.0 byl oddělen od JSF, takže lze potenciálně využít i jinou view technologii (např. GWT, Google Web Toolkit).
 +
 +
=== JBoss RichFaces ===
 +
Nejde o framework, ale o předpřipravené JSF komponenty, využívající Ajax. Lze je snadno integrovat do jakéhokoli JSF projektu/frameworku.
  
 
== JSF a AJAX ==
 
== JSF a AJAX ==
* Ajax4JSF - jednoduchá knihovna, která zapouzdřuje Ajax průhledně do JSF
+
* Ajax4JSF - jednoduchá knihovna, která zapouzdřuje [[Ajax]] průhledně do JSF
  
 
== JSF a Portlety (JSR 168) ==
 
== JSF a Portlety (JSR 168) ==
Řádka 410: Řádka 464:
 
Mimo to existují pro jednotlivé portály JSF Portlet Bridge frameworky, které se snaží překlenout rozdíly mezi JSF a portlety.
 
Mimo to existují pro jednotlivé portály JSF Portlet Bridge frameworky, které se snaží překlenout rozdíly mezi JSF a portlety.
 
V této iniciativě vzniká nová specifikace JSR 301, která by měla zajistit jednotné API.
 
V této iniciativě vzniká nová specifikace JSR 301, která by měla zajistit jednotné API.
 +
  
 
=== MyFaces ===
 
=== MyFaces ===

Aktuální verze z 23. 4. 2013, 16:59


Úvod

Java Server Faces je komponentově orientovaný webový rámec (pull-based), je tedy jiného typu než klasický přístup Struts či Stripes. V klasickém pojetí je stránka obsluhována jako celek, je zde velký důraz na usnadnění obsluhy paradigmu HTTP Request-Response. Komponentový přístup JSF deleguje obsluhu stránky jako takové na jednotlivé komponenty, které si svoje místečko na webové stránce obhospodařují samy.

JSF, stejně jako většina webových frameworků, je postaveno na Servlet API a JSP. JSP jako view vrstva může být ovšem nahrazena prakticky čímkoliv. Pro JSP jsou k dispozici dvě knihovny značek (taglib).

Jednou z nejčastěji kritizovaných vlastností JSF je obecnost, se kterou lze v JSF vyměnit prakticky kteroukoliv podčást za jinou. Ve spojení s jednoduchostí použití (o což se JSF snaží) je pod povrchem JSF schováno hodně hodně hodně kódu, který je těžké pochopit a při chybě těžké opravit.

Specifikaci vytvořili:

  • 1.0 - Craig McClanahan (tvůrce Struts, proto se nedivte, pokud poznáte určité podobnosti se Struts ;) a Ed Burns
  • 1.1 - Craig McClanahan, Ed Burns, Roger Kitain
  • 1.2 - Ed Burns, Roger Kitain
  • 2.0 - Ed Burns, Roger Kitain
  • 2.1 - Ed Burns, Roger Kitain (Aktuální verze, jen drobné úpravy oproti 2.0)

Výhody (proč ano)

  • tzv. "standard", tj. je součástí Java EE
  • podpora od výrobců vývojových nástrojů
  • efektivní při základním použití
  • dostatek dokumentace v rámci komunity
  • používané <==> vyžadováno sw společnostmi


Nevýhody (proč ne)

  • je nutné doplnit o dva další frameworky (Seam,Facelets) aby bylo použitelné
  • komplikované až těžkopádné při snaze využít specialitek JSF
  • renderery si musí vypomáhat JavaScriptem
  • navigace je založena jen na POST

Jsfkoule.gif

Popis, jak to funguje

Pro JSF existuje jeden obslužný servlet FacesServlet, který zajišťuje, že všechny komponenty projdou definovaným životním cyklem během života HTTP požadavku. Pro jednoduchý náhled můžeme dále uvažovat již jen o životním cyklu a o komponentách.


MVC

JSF jsou silně založeny na konceptu MVC, i když se jedná pouze o prezentační vrstvu třívrstvé architektury.

  • Model vrstva by měla být tvořena ideálně pomocí java beanů nesoucí data. V JEE 5 aplikaci se jedná např. o entitní beany.
  • View vrstva používá RenderKit a JSP stránky. V JSP stránkách se vytvoří strom komponent z dvou knihoven JSF (Core a HTML RenderKit) a případných dalších komponent 3. stran. Strom komponent je provázán na Model a Controller pomocí EL (Expression Language). Rámec Facelets řeší převážně tuto vrstvu.
  • Controller vrstva je tvořena pomocí tzv. Backing (Managed, jak se vám líbí) beans, což jsou obyčejné JavaBeans, které obstarávají přístup k Modelu a mají za úkol řídit běh aplikace. Tyto beany lze nahradit pomocí EJB 3.0, nicméně toto řešení přináší smíchání prezentační a aplikační vrstvy třívrstvé aplikace! Rámec Seam řeší především tuto vrstvu.

Model Vrstva

Model vrstva není přímo modelována pomocí JSF, ale velice často se přebírá z aplikace, kdy jsou entitní beany uloženy v Backing beanu a pomocí EL jsou v JSP vyzobávány do formuláře jednotlivá políčka. I v JSF existují ovšem třídy, které slouží jako proxy (wrappery) pro JSF komponentky.

  • DataModel slouží pro operace nad tabulkovými daty, jeho potomci (ArrayDataModel, ListDataModel, ResultDataModel, ResultSetDataModel, ScalarDataModel) umožňují konkrétní adaptaci na aplikační data.
  • SelectItem s potomkem SelectItemGroup slouží jako model pro UISelectMany a UISelectOne komponentky.

Controller Vrstva = Backing Bean

Backing bean, jak bylo již uvedeno, je obvykle normální JavaBean. Tento bean zpracovává události, řídí běh prezentační vrstvy a interaguje s business vrstvou aplikace.

  • Musí obsahovat bezparametrický konstruktor, aby jej mohlo JSF dynamicky za běhu instanciovat.
  • Většinou obsahuje Gettery/Settery pro přístup k Modelu.
  • Obsahuje validační metody.
  • Obsahuje metody pro zpracování událostí.
  • Protože se jedná o Controller obsahuje bezparametrické metody řídící běh aplikace, které vždy vrací akci (String) pro navigaci.
 
public class IndexManagedBean {
    private String zvire;
 
    public String getZvire() {
        return zvire;
    }
 
    public void setZvire(String zvire) {
        this.zvire = zvire;
    }
 
    public String processSubmit(){
        System.out.println("Zvire je: " + zvire);
        return "OK";
    }
}

V deklarativní konfiguraci beanu v faces-config.xml můžeme nastavit scope beanu a dokonce i fiktivní atributy, které v definici třídy neexistují:

 
    <managed-bean>
        <managed-bean-name>indexBean</managed-bean-name>
        <managed-bean-class>jsf.backingbean.IndexManagedBean</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
        <managed-property>
            <property-name>nonExistingProperty</property-name>
            <property-class>long</property-class>
            <value>0</value>
        </managed-property>
    </managed-bean>

View vrstva

View se skládá z UI komponent a JSP stránek, kde jsou jednotlivé komponenty seskládány do stromu, který je přístupný pomocí FacesContext.getCurrentInstance().getViewRoot(). Nicméně lze místo JSP použít i jiné technologie, které je ovšem třeba zvlášť deklarovat.

User Interface Components

JSF přináší základní sadu komponentek, které by měly pokrývat většinu potřeb formulářových aplikací.

  • Komponenta jako taková je java třída rozšiřují UIComponent.
  • K ní by měl existovat Renderer, který danou komponentu vykresluje. Každý Renderer vždy patří do nějakého RenderKitu. Lze ovšem i udělat výjimky, kdy se vykresluje sám potomek UIComponent bez Rendereru. Jedna komponentka může mít dokonce více rendererů (např. UICommand má HTMLCommandLink a HTMLCommandButton).
  • Aby se komponenta dala použít v JSP, jsou v JSF vytvořeny dva základní tagliby: JSF Core (prefix f:) a JSF HTML (prefix h:). Ty obsahují Tagy pro jednotlivé komponenty.
JSP

V rámci JSP se vkládají jednotlivé komponentky na stránku. Všechny komponentky na stránce musí být umístěny do <f:view> tagu. JSF HTML obsahuje všechny důležité komponentky pro formulářové aplikace, které jsou většinou poděděny z UIInput a UIOutput. Formulářové komponentky musí být uvnitř <h:form> tagu.

Existují dva používané způsoby, jak používat komponentky v aplikaci.

  • Komponentka je pouze na stránce a pomocí EL se váže pouze Model:
 
  <h:form>
    <h:inputText value="#{indexBean.zvire}" /><br />
    <h:inputText value="#{indexBean.nonExistingProperty}" /><br />
    <h:commandButton action="#{indexBean.processSubmit}" />
  </h:form>
  • Komponentka je v Backing beanu a referencuje se z JSP:
 
    private HTMLInputText inputText = new HTMLInputText();
    public void setInputText(HTMLInputText inputText){
        this.inputText = inputText;
    }
    public HTMLInputText getInputText(){
        return this.inputText;
    }
<h:inputText binding="#{mujBackingBean.inputText}"/>

Konfigurace JSF aplikace

Aby kontejner dokázal obsloužit JSF stránky, je třeba zadeklarovat ve web.xml FacesServlet a namapovat jej na nějakou (jakoukoliv) URL:

 
    <servlet>
        <display-name>FacesServlet</display-name>
        <servlet-name>FacesServlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>FacesServlet</servlet-name>
        <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>

Takže když příjde Request, webový kontejner spustí FacesServlet, který již sám řídí běh aplikace.

Celá JSF aplikace se konfiguruje pomocí souboru /WEB-INF/faces-config.xml.


 
 //faces-config.xml
 <managed-bean>
    <description>
      The backing bean that backs up the guessNumber Web application
    </description>
    <managed-bean-name>UserNumberBean</managed-bean-name>
    <managed-bean-class>guessNumber.UserNumberBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
    <managed-property>
      <property-name>minimum</property-name>
      <property-class>long</property-class>
      <value>0</value>
    </managed-property>
    <managed-property>
      <property-name>maximum</property-name>
      <property-class>long</property-class>
      <value>10</value>
    </managed-property>
  </managed-bean>

Ve verzi Java EE 5.0 (~JSF 1.2) byl sjednocen JSTL EL a JSF EL do jednoho Unified EL jazyka:

  • výrazy ${neco} označují výrazy pro čtení
  • výrazy #{neco} označují výrazy, které umožňují i zápis, volání metod a odložené vyhodnocování výrazů

K výše uvedenému managed JavaBeanu můžete tedy přistupovat pomocí #{UserNumberBean}

 
 <%@ page contentType="text/html; charset=utf-8" %>
 <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
 <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
 <body bgcolor="white">
    <f:view>
            <h:form id="mujForm" >
                max: <h:outputText value="#{UserNumberBean.maximum}"/> <br>
                min: <h:outputText value="#{UserNumberBean.minimum}"/> <br>
                Novy max: <h:inputText id="newMax" value="#{UserNumberBean.maximum}" > </h:inputText><br>
                Novy min: <h:inputText id="newMin" value="#{UserNumberBean.minimum}" > </h:inputText><br>
                <h:commandButton id="submit" action="success" value="Submit" />
            </h:form>
        </f:view>
 </body>

Data jsou ve dvou formách - ve tvaru pro model, např. integer, a ve tvaru pro prezentaci, typicky řetězce. JSF zajišťuje konverze mezi těmito dvěma tvary.

Navigace

Navigace mezi stránkami se konfiguruje opět ve /WEB-INF/faces-config.xml.

 
 <navigation-rule>
    <from-view-id>/moje.jsp</from-view-id>
    <navigation-case>
      <from-outcome>dobre</from-outcome>
      <to-view-id>/dalsiStranka.jsp</to-view-id>
    </navigation-case>
    <navigation-case>
      <from-outcome>spatne</from-outcome>
      <to-view-id>/spatne.jsp</to-view-id>
    </navigation-case>
    <navigation-case>
      <from-outcome>dalsi</from-outcome>
      <to-view-id>/dalsiStranka.jsp</to-view-id>
    </navigation-case>
 </navigation-rule>

Ve stránce pak lze specifikovat přechod přímo:

<h:commandButton id="submit-dalsi" action="dalsi" value="Jdi dal" />

či typicky rozhodnout v kódu:

 
    public String jakDopadlo() {
        if(...) return "dobre";
        else return "spatne";
    }
<h:commandButton id="submit" action="#{mujManagedBean.jakDopadlo}" value="Proved akci" />


Lokalizace

  1. JSF podporují lokalizaci pomocí <f:loadBundle /> tagu v rámci JSP
  2. a pomocí deklarace v faces-config.xml, kde se mimo nastavuje ResourceBundle pro JSF komponentky

V rámci JSP se používá:

 
    <f:loadBundle basename="jsf.resource.Messages" var="bundle"/>
    ...
    <title><h:outputText value="#{bundle.welcome_title}" /></title>

nebo

 
    <f:loadBundle basename="js.resource.Messages" var="bundle"/>
    ...
    <title>${bundle.welcome_title}</title>

Bohužel <f:loadBundle /> nepodporuje tečkovou notaci kvůli EL, takže nemůžeme použít například <h:outputText value="#{bundle.welcomeJSP.title}" />

Deklarace ve faces-config.xml:

 
    <application>
        <locale-config>
            <default-locale>en</default-locale>
            <supported-locale>cs</supported-locale>
        </locale-config>
        <message-bundle>jsf.resource.FacesMessages</message-bundle>
    </application>

Běh

Jak již bylo několikrát zmíněno, běh a životní cyklus řídí FacesServlet. Aby mohlo být předáno řízení FacesServletu, je třeba, aby příchozí request obsahoval URL, na které je FacesServlet namapován. Pokud je výchozí JSP index.jsp, potom se používá např. tato konstrukce:

 
<!-- web.xml -->
    <welcome-file-list>
        <welcome-file>redirectToJSF.jsp</welcome-file>
    </welcome-file-list>
 
//redirectToJSF.jsp
    <% response.sendRedirect("index.jsf"); %>

Nyní již odchytí běh FacesServlet, který použije třídu Lifecycle pro spuštění životního cyklu request-response ([JSF#.C5.BDivotn.C3.AD_cyklus_str.C3.A1nky]]).

Životní cyklus stránky

Zpracování JSF stránky je komplikovanější než JSP stránky samotné. Viz obrázek.

JsfIntro-lifecycle.gif

Rozlišují se dva druhy volání:

  • initial requests - při prvním zobrazené stránky, provádí se pouze restore view a render response fáze
  • postbacks - uživatel odeslal formulář na stránce, aktivují se všechny fáze.

Životní cyklus řídí FacesServlet (normální Servlet, je třeba deklarovat v rámci web.xml), který provádí jednotlivé fáze:

  1. Restore View - obnoví (vytvoří) strom komponent
  2. Apply Request Values; process events - nastaví hodnoty z requestu jednotlivým komponentám ve stromu a zpracuje možné události
  3. Process Validation; process events - provede validaci stromu komponent a zpracuje možné události
  4. Update Model Values; process events - nastaví zvalidované hodnoty ze stromu komponent do Modelu a zpracuje možné události
  5. Invoke Application; process events - předání řízení Controlleru, tj. backing beanům a zpracuje možné události
  6. Render Response - vygeneruje strom komponent pomocí daného RenderKitu


Validace

Validace se provádějí ve fázi Process Validation (obr. JSF#.C5.BDivotn.C3.AD_cyklus_str.C3.A1nky). Po skončení fáze se testuje, zdali existují chyby. Pokud jakákoliv validace neprojde, pokračuje se fází Render Response a řízení se předává zpět webovému kontejneru.

Chyby (hlášky) je poté možné zobrazit pomocí

  1. <h:message for="idNejakehoInputku" />, který vypíše chyby přímo inputku s uvedeným ID
  2. nebo je možné vypsat všechny zprávy pomocí <h:messages />

Je třeba dát pozor na validaci povinnosti. Ta se definuje pomocí atributu required v rámci UIInput. Pokud tato validace neprojde, specifikuje se výstupní hláška pomocí property javax.faces.component.UIInput.REQUIRED v resource bundlu, který je deklarován ve faces-config.xml.

Pro validace lze obecně použít dvě cesty.

Validace metodou

Validace se provede pomocí metody v Backing beanu, kdy se komponentka implementující EditableValueHolder prováže přímo na speciální metodu Backing beanu.

Number: <h:inputText validator="#{mujBackingBean.validateNumber}" ... 
 
    // validation against 42!
    public void validateNumber(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        if(!(value instanceof Integer)){
            throw new ValidatorException(new FacesMessage("Wrong data type!"));
        }
        
        Integer val = (Integer) value;
                
        if(val.intValue() != 42){
            ResourceBundle res = ResourceBundle.getBundle("jsf.resource.validation", FacesContext.getCurrentInstance().getViewRoot().getLocale());
            FacesMessage message = new FacesMessage(res.getString("validation.numberNot42"));
            throw new ValidatorException(message);
        }
    }

Validace validátorem

JSF obsahuje několik vestavěných validátorů, které se nacházejí v JSF Core (prefix f:)

  • LengthValidator - validuje délku Stringu v rámci UIInput,
  • LongRangeValidator a DoubleRangeValidator - validují povolený rozsah Long či Double v rámci UIInput

Hlášky při špatné validaci se opět nastavují v ResourceBundle v rámci faces-config.xml, konkrétní názvy polí jsou v JavaDocu příslušné třídy.

Typické použití:

 
    <h:inputText id="username" value="#{LoginBean.username}" required="true"> 
      <f:validateLength maximum="15" minimum="3" /> 
    </h:inputText>

JSF podporuje také vytvořit vlastní validátor. Tvorba ovšem podmiňuje všechny tyto kroky :(, což je možná důvod, že se prakticky nové validátory nepoužívají:

  • Vytvořit Java třídu implementující rozhraní Validator
  • Implementovat metodu public void validate(FacesContext context, UIComponent component, java.lang.Object value) throws ValidatorException, která při chybné validaci vyhodí vyjímku ValidatorException.
  • Definovat validátor a jeho položky ve faces-config.xml, např.
 
    <validator>
        <validator-id>RegExpValidator</validator-id>
        <validator-class>jsf.validator.RegExpValidator</validator-class>
        <property>
            <property-name>regExp</property-name>
            <property-class>java.lang.String</property-class>
        </property>
    </validator>
  • Vytvořit Tag pro validátor rozšiřující ValidatorTag
 
    private String regExp; 
    public void setRegExp(String p){this.regExp = p;}
    public String getRegExp(){return this.regExp;}
    public RegExpValidatorTag(){
        super();
        super.setValidatorId("RegExpValidator"); // definovano ve faces-config.xml
    }
    protected Validator createValidator() throws JspException {
        RegExpValidator result =
         (RegExpValidator) super.createValidator();
        result.setRegExp(regExp);
        return result;
    }
  • Přidat Tag do nějakého našeho TLD (např. /WEB-INF/nase.tld)
 
    <tag>
        <name>regExpValidator</name>
        <tag-class>jsf.tags.RegExpValidatorTag</tag-class>
        <body-content>empty</body-content>
        <display-name>regExpValidator</display-name>
        <description></description>
        <attribute>
            <name>regExp</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
  • Použít validátor
 
    <%@ taglib uri="/WEB-INF/neco.tld" prefix="neco" %>
    ...
    <h:inputText ...
        <neco:regExpValidator regExp=".+@.+\\..+" />
    </h:inputText>


Konvertory

Konvertory slouží pro převod aplikačního Model objektu do Stringu pro použití View vrstvou a naopak. JSF obsahuje konvertory pro všechny primitivní typy.

Pokud chceme použít neprimitivní typ (nějaký náš Model bean) na stránce, který nelze jednoduše zabalit např. do SelectItem (viz JSF#Model_Vrstva), je třeba vytvořit vlastní konvertor. K tomu je třeba:

  • Implementovat rozhraní Converter, v něm metody
 
    public java.lang.Object getAsObject(javax.faces.context.FacesContext context,
                                        javax.faces.component.UIComponent component,
                                        java.lang.String value)
    public java.lang.String getAsString(javax.faces.context.FacesContext context,
                                        javax.faces.component.UIComponent component,
                                        java.lang.Object value)
  • Zadeklarovat jej ve faces-config.xml
 
    <converter>
        <converter-id>converterNasehoBeanu</converter-id>
        <converter-class>jsf.converters.ConverterNasehoBeanu</converter-class>
    </converter>
  • Použít v JSP (2 způsoby)
 
    <h:selectOneRadio converter="converterNasehoBeanu" value="#{mujBackingBean.vybranyNasBean}">
        <f:selectItems value="#{mujBackingBean.seznamNasichBeanu}"/>
    </h:selectOneRadio>
 
    <h:selectOneRadio value="#{mujBackingBean.vybranyNasBean}">
        <f:selectItems value="#{mujBackingBean.seznamNasichBeanu}"/>
        <f:converter converterId="converterNasehoBeanu" />
    </h:selectOneRadio>

Implementace JSF

V současné době se používají dvě hlavní implementace JSF.

  • RI (reference implementation) je od Sunu ve verzi 1.2
  • MyFaces od Apache je ve verzi 1.2


3rd party komponentky nad JSF

Frameworky pracující s JSF

Facelets

Pozn.: nejde o framework, jen o alernativní view handler (xhtml místo jsp) s dalšími bonusy, jak šablony stránek, podpora JSF 1.2 bez vyžadování JSP 2.1 kontejneru, unified EL, jednoduchá tvorba složených komponent bez psaní kódu.

Seam

  • ONJava.com: JBoss Seam by Thomas Heute, 03/15/2006]

Podle všeho moc pěkný framework, který obohacuje prezenční vrstvu o celou řadu skvělých věcí, jako koncept konverzace (řada za sebou jdoucích a souvisejících requestů - snaží se řešit problém současného užití více oken prohlížeče). Silně inspiroval nové JSR 311 zv. Web Beans.

Od verze 2.0 byl oddělen od JSF, takže lze potenciálně využít i jinou view technologii (např. GWT, Google Web Toolkit).

JBoss RichFaces

Nejde o framework, ale o předpřipravené JSF komponenty, využívající Ajax. Lze je snadno integrovat do jakéhokoli JSF projektu/frameworku.

JSF a AJAX

  • Ajax4JSF - jednoduchá knihovna, která zapouzdřuje Ajax průhledně do JSF

JSF a Portlety (JSR 168)

Pro obě implementace existují předvytvořené portlety, které slouží jako proxy mezi Portlet Containerem a web Containerem. Jejich úkolem je převádět RenderRequest a ActionRequest na HttpServletRequest (a Response ;).

Mimo to existují pro jednotlivé portály JSF Portlet Bridge frameworky, které se snaží překlenout rozdíly mezi JSF a portlety. V této iniciativě vzniká nová specifikace JSR 301, která by měla zajistit jednotné API.


MyFaces

Obsahují třídu MyFacesGenericPortlet, který slouží jako proxy. How-To

Sun RI

Obsahují javaserverfaces_portlet.class balík pro zprovoznění JSF v rámci portletů. How-To

Budoucnost

Zdroje