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é.
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:
(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.