JSF komponenta

Tento článek popisuje vývoj jednoduché komponenty v
Java Server Faces (JSF) verze 1.2. Nejprve si
ukážeme, co tvoří JSF komponentu a pak jednoduchou
komponentu naimplementujeme. Technologie JSF je
postavena nad Java Server Pages (JSP), jejichž
znalost dále předpokládám.

Ukázková komponenta bude generovat posloupnost
náhodných čísel, která bude sloužit jako nápověda pro
sázkaře. Pokud používáte NetBeans 6.5, začněte
vytvořením nové webové aplikace. Při vytváření
projektu nezapomeňte zatrhnou podporu JSF.

JSF komponenta se skládá z těchto částí:

  • UIComponent class – třída, která
    obsahuje "logiku" komponenty. Její
    instance jsou součástí stromu komponent, který se
    vytváří na serveru po příchodu JSF požadavku.
  • Renderer class – třída zodpovědná za
    převod (rendering) komponenty do HTML či jiného
    značkovacího jazyka. Pokud se o tento převod
    postará sama třída komponenty (UIComponent class),
    může Renderer chybět.
  • Tag class – slouží k odkazu na
    komponentu v JSP stránce. Je také zodpovědný za
    předávání parametrů do třídy komponenty.
  • Tag Library Descriptor (TLD) – popis
    tagu, tj. jeho jméno, třída a atributy.

Začneme implementací logiky komponenty. Třída LottoComponent bude potomkem třídy UIComponentBase, která je potomkem UIComponent. Protože nebudeme používat
Renderer, postará se o převod do HTML sama
komponenta. K tomu slouží metody encodeBegin, encodeEnd a
příp. i encodeChildren. V našem
příkladu vystačíme s encodeBegin.

public class LottoComponent extends UIComponentBase {

private Random rand = new Random();

private int number = 6;

@Override

public void encodeBegin(FacesContext fc) throws IOException {

ResponseWriter w = fc.getResponseWriter();

w.startElement("p", this); // zapíše <p>

for (int i = 0; i < number; i++) { // zapisuje čísla z intervalu <1,40>

w.writeText(1 + rand.nextInt(40) + " ", null);

}

w.endElement("p"); // zapíše </p>

}

@Override

public String getFamily() {

return "LottoFamily";

}

}

Metoda getFamily vrací jméno rodiny
komponent, do níž komponenta patří. Rodina komponenty
se používá při výběru Rendereru. Protože v našem
příkladu Renderer nepoužíváme, metoda může vracet
libovolný řetězec.

Dále vložíme do faces-config.xml element component:

<faces-config version="1.2"

xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">

<component>

<component-type>Lotto</component-type>

<component-class>lotto.LottoComponent</component-class>

</component>

</faces-config>

A naimplementujeme Tag:

public class LottoTag extends UIComponentELTag {

@Override

public String getComponentType() {

return "Lotto"; // vrací to, co je uvedeno v elementu component-type ve faces-config.xml

}

@Override

public String getRendererType() {

return null; // Renderer v tomto příkladě nepoužíváme

}

}

Třída LottoTag je potomkem UIComponentELTag, která je určena pro
vytváření JSF tagů. JSF tagy jsou
"vylepšené" JSP tagy. Umí navíc např. Expression Language.

Tag Library Descriptor obsahuje popis
tagu:

<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee">

<tlib-version>1.0</tlib-version>

<short-name>lotto</short-name>

<uri>/WEB-INF/Lotto</uri>

<tag>

<name>tips</name>

<tag-class>lotto.LottoTag</tag-class>

</tag>

</taglib>

Element short-name obsahuje
doporučený prefix pro tagy z této knihovny a element
uri je identifikátor knihovny. Pomocí
uri se na knihovnu budeme odkazovat z
JSP stránky. V elementu name je jméno
tagu a v elementu tag-class je jméno
třídy tagu.

Teď už můžeme tag použít v JSF stránce:

<%@taglib prefix="lotto" uri="/WEB-INF/Lotto"%>

...

<f:view>

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>Lotto</title>

</head>

<body>

<h1>Lotto</h1>

<lotto:tips/>

</body>

</html>

</f:view>

Výhody komponentového modelu se naplno projeví
tehdy, když použijeme tag opakovaně. Můžeme jej
použít na té samé stránce i na jiné.

JSFCompParts

Atributy

Dále přidáme naší komponentě atribut number, kterým řekneme, kolik čísel chceme
generovat. Pokud atribut nebude uveden, bude
komponenta generovat šest čísel jako dříve.

Do TDL souboru přidáme popis atributu:

<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee">

<tlib-version>1.0</tlib-version>

<short-name>lotto</short-name>

<uri>/WEB-INF/Lotto</uri>

<tag>

<name>tips</name>

<tag-class>lotto.LottoTag</tag-class>

<attribute>

<name>number</name>

<description>The number of tips</description>

</attribute>

</tag>

</taglib>

Ve třídě LottoTag přibyde property
number a metody setProperties a release:

public class LottoTag extends UIComponentELTag {

private String number;

@Override

protected void setProperties(UIComponent component) {

super.setProperties(component);

if (number != null) {

component.getAttributes().put("number", number);

}

}

@Override

public void release() {

super.release();

number = null;

}

@Override

public String getComponentType() {

return "Lotto"; }

@Override

public String getRendererType() {

return null;

}

public String getNumber() {

return number;

}

public void setNumber(String number) {

this.number = number;

}

}

Metoda setProperties je volána po
vytvoření komponenty před jejím přidáním do stromu
komponent. Má za úkol přenos parametrů z tagu do
komponenty. Metoda release je volána
po ukončení zpracování tagu. Měla by objekt vrátit do
stavu, ve kterém byl těsně po vytvoření. Při
opakovaném výskytu stejného tagu se totiž nemusí
vytvářet vždy nová instance, ale starší instance
mohou být "recyklovány".

Ve třídě LottoComponent upravíme
metodu encodeBegin, aby používala
atribut number:

@Override

public void encodeBegin(FacesContext fc) throws IOException {

String s = (String) getAttributes().get("number");

if (s != null) {

number = Integer.parseInt(s);

}

ResponseWriter w = fc.getResponseWriter();

for (int i = 0; i < number; i++) {

w.writeText(1 + rand.nextInt(40) + " ", null);

}

}

V JSP stránce pak můžeme použít u tips atribut number:

<lotto:tips number="4"/>

Expression Language

Dále komponentu rozšíříme o podporu Expression
Language (EL)
. Tj. přidáme možnost stanovit
hodnotu atributu number pomocí výrazu
v EL:

<lotto:tips number="#{testManagedBean.size}"/>

Nejprve upravíme TLD:

<tag>

<name>tips</name>

<tag-class>lotto.LottoTag</tag-class>

<attribute>

<name>number</name>

<deferred-value>

<type>Integer</type>

</deferred-value>

<description>The number of tips</description>

</attribute>

</tag>

Pak drobně změníme třídu LottoTag:

public class LottoTag extends UIComponentELTag {

private ValueExpression number;

@Override

protected void setProperties(UIComponent component) {

super.setProperties(component);

if (number != null) {

component.getAttributes().put("number", number);

}

}

@Override

public void release() {

super.release();

number = null;

}

@Override

public String getComponentType() {

return "Lotto";

}

@Override

public String getRendererType() {

return null;

}

public ValueExpression getNumber() {

return number;

}

public void setNumber(ValueExpression number) {

this.number = number;

}

}

A nakonec ještě malá úprava metody encodeBegin ve třídě LottoComponent:

@Override

public void encodeBegin(FacesContext fc) throws IOException {

ValueExpression ve = (ValueExpression) getAttributes().get("number");

if (ve != null) {

number = (Integer) ve.getValue(fc.getELContext());

}

ResponseWriter w = fc.getResponseWriter();

for (int i = 0; i < number; i++) {

w.writeText(1 + rand.nextInt(40) + " ", null);

}

}

A je hotovo. Na závěr si ještě ukážeme, jak
vytvořit knihovnu, kterou lze snadno připojit k
webové aplikaci. Vytvoříme jar, který bude mít tuto
strukturu:

complib

(MANIFEST.MF byl vložen automaticky programem jar).
Takto vytvořenou knihovnu pak k projektu připojíme
nakopírováním jaru do adresáře WEB-INF/lib.