Java Server Faces: Porovnání verzí

Z FI WIKI
Přejít na: navigace, hledání
m (Konfigurace JSF aplikace: - preklep)
(Nevýhody)
Řádka 33: Řádka 33:
 
* podpora od výrobců vývojových nástrojů
 
* podpora od výrobců vývojových nástrojů
  
=== Nevýhody ===
+
<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
* komplikované
+
* je nutné doplnit o dva další frameworky (Seam,Facelets) aby bylo použitelné
+
* renderery si musí vypomáhat JavaScriptem
+
* navigace je založena jen na POST
+
  
 
== Popis, jak to funguje ==
 
== Popis, jak to funguje ==

Verze z 26. 12. 2007, 13:33


Úvod

Java Server Faces je rámec postavený přímo na Servlet API. Může používat JSP, ale nemusí. 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ů, 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 UICommand se může zobrazit

  • jako hyperlink pomocí značky <h:commandLink>
  • jako tlačítko pomocí značky <h:commandButton>

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:

  • 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 (zatím plenky) - Ed Burns, Roger Kitain

Výhody

  • tzv. "standard", tj. je součástí JavaEE
  • podpora od výrobců vývojových nástrojů

<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

Popis, jak to funguje

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 komponenty 3. stran. Strom komponent je provázán na Model a Controller pomocí EL (Expression Language).
  • 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.

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 obykle normální java bean. 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.

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>indexForm</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>

V JSP se poté odkazujeme na bean pomocí EL (v rámci IndexManagedBean musí existovat metoda public String processSubmit()):

 
  <h:form>
    <h:inputText value="#{indexForm.nonExistingProperty}" />
    <h:commandButton action="#{indexForm.processSubmit}" />
  </h:form>

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í sadů 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 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).
  • 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ř <f: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:
 
    private String ter;
    public void setter(String ter){this.ter = ter;}
    public String getter(String ter){this.ter = ter;}
Ter: <h:inputText value="#{mujBackingBean.ter}" />
  • Komponentka je v Backing beanu a referencuje se z JSP:
 
    private HTMLInputText inputText = new HTMLInputText();
    public void setInputText(HTMLInputText inputText){this.inputText = inputText;}
    public String getInputText(HTMLInputText inputText){this.inputText = inputText;}
    private String ter;
    public void setter(String ter){this.ter = ter;}
    public String getter(String ter){this.ter = ter;}
Ter: <h:inputText value="#{mujBackingBean.ter}" 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. 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 ;)).


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 JavaEE 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 např. pomocí

 
 <%@ 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>/dobre.jsp</to-view-id>
    </navigation-case>
    <navigation-case>
      <from-outcome>spatne</from-outcome>
      <to-view-id>/spatne.jsp</to-view-id>
    </navigation-case>
 </navigation-rule>

Ve stránce pak lze specifikovat, že výstup z akce poskytuje určité metoda:

<h:commandButton id="submit" action="#{mujManagedBean.jakDopadlo}" value="Submit" />

a metoda musí vracet jeden z očekávaných řetězců:

 
    public String jakDopadlo() {
        if(...) return "dobre";
        else return "spatne";
    }

Ž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 :( :

  • 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>

Lokalizace

  1. JSF podporují lokalizaci a internacionalizaci 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]]).

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.1, chystá se uvolnění 1.2 (zdroje již prošly TCL)


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).

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