Saját JSF UI-komponens létrehozása

A Wikipédiából, a szabad enciklopédiából
(JSF saját UI komponens szócikkből átirányítva)

A JavaServer Faces-nek (JSF) nemcsak az erőssége, hogy egyszerű módon teszi lehetővé J2EE komponens alapú webrendszerek, webprogramok létrehozását, hanem az is, hogy lényegében egy igen rugalmas API, ami széles körű testre szabhatóságot biztosít. Ez a cikk a saját JSF UI (felhasználói felület) létrehozásának folyamatába enged betekintést.

Célközönség[szerkesztés]

Először is tisztáznunk kell, hogy ezen értekezés olyan fejlesztők számára készült, akik már teljes mértékben értik a JSF-alkalmazás fejlesztési folyamatát, ugyanakkor meg szeretnék tanulni, hogyan is fogjanak neki egy saját JSF UI-komponens megalkotásához. A cikk megmutatja, hogyan hozzuk létre egy egyszerű "HelloWorld" UI-komponenst.

Mikor fejlesszünk magunk komponenseket[szerkesztés]

Mielőtt nagy hévvel belevetnénk magunkat a fejlesztésbe, érdemes szétnézni a neten, kutatni egy kicsit, hogy valóban szükséges-e magunknak létre hozni az egyedi komponensünket, vagy esetleg létezik-e már hasonló. Az "alap" JSF UI-komponens könyvtár igen szegényesnek mondható (mind az UI- és nem UI-komponensek terén csak az alap dolgok adottak, mint pl.: ki- és bemeneti mezők vagy menügombok, illetve a nem UI-oldalról adat validáció és átalakítás), de csupán a Google segítségével számos hasznos komponens könyvtárra lelhetünk rá.[1]

  • MyFaces - általános célú és nyílt forrású komponens könyvtár[2]
  • Oracle's ADF Faces - egy igen széles körű és hasznos UI-komponens könyvtár[3]

Fontos azonban utánajárnunk, hogy az adott könyvtár milyen feltételekkel használható, ugyanakkor nagyon sok nyílt forráskódú, ingyenes könyvtár létezik a neten. Ha végül csak az önálló fejlesztés mellett döntünk, a továbbiakban bemutatom, hogyan is kezdjünk neki a procedúrának.

A JSF UI-komponensek részei[szerkesztés]

Egy JSF UI-komponenst általában alkomponensekre bontva definiálunk. Minden ilyen al-komponensnek megvan a maga feladata mint pl.: a komponens megjelenítése, az input adat validációja (ellenőrzése) vagy esetleges egyéb konverziók (pl.: dátumból milyen formátumú szöveg legyen). A al-komponensekre bontás sokszor a bonyolultság látszatát keltheti a kezdő fejlesztők számára, ugyanakkor ez is a JSF egyik erőssége, hogy minden UI-komponenst testre tudunk szabni a al-komponensein keresztül. (Tehát például a már meglévőt alakítjuk át, így nem kell nulláról fejleszteni.) A JSF ezen tulajdonsága teszi lehetővé a fejlesztők számára, hogy a komplexitást csökkentve képesek legyenek multi-kliens web alkalmazásokat írni. A JSF UI-komponens részei:

  • UIComponent Class - Egy Java-osztály amely származhat az UIComponentBase (alap) osztályból vagy egy már meglévő JSF UIComponent osztályra építkezve hozzuk létre (ilyen lehet pl.:outputText). Ez az osztály tartalmazza a komponens alap logikáját. Tartalmazhatja a megjelenítés logikáját is de azt akár külön osztályban megvalósítva is létrehozhatjuk.
  • Renderer Class - Ez az osztály a komponens megjelenítésért felelős kódot tartalmazza (csak és kizárólag). Tehát például ebben az osztályban definiálhatjuk egy nyomógomb megjelenítését (stb.). Több megjelenítést is definiálhatunk, például kliens típusoknak megfelelően megadhatjuk a kinézetet hagyományos asztali környezetben futó web-böngészőkre és megadhatjuk pda-ra is.
  • UI Component Tag Class - Ez egy JSP tag kezelő osztály (Tag Handler Class)amely lehetővé teszi az UI-komponens használatát egy JSP-ben. (Ez az osztály csak akkor szükséges ha JSP-t használó környezetben dolgozunk, de tartsuk észben hogy az UI-komponenst használhatjuk nem JSP-s környezetben is.)
  • Tag Library Descriptor File - Ez egy standard J2EE JSP tag könyvtárat leíró fájl (tld)amely összekapcsolja a tag kezelő osztályokat az adott tag-ekkel egy JSP oldalon. (Csak JSP esetén szükséges.)
  • Associated helper classes - Ezen osztályokba azon standard vagy egyénileg definiált segítő osztályok mint pl.: Converters, Validators, ActionListeners...

Példa egy egyszerű "HelloWord" saját UI-komponensre[szerkesztés]

Mielőtt megmutatnám, hogyan építsük fel a "HelloWorld" saját komponensünket, bemutatom annak használatát. Hogy megfelelően beépítsük a komponensünket és használjuk is azt szükségünk van egy JSF alapú JSP oldalra, továbbá szükségünk van egy tag könyvtárra(tld) amely tartalmazza a "jsfhello" címkét. Így kapcsoljuk össze a tag-et a saját komponensünkkel. Hogy használjuk a "jsfhello" tag-et csak egyszerűen szúrjuk azt be a JSP oldal body-jába és állítsuk be a "hellomsg" attribútum értékét.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<%@ page contentType="text/html;charset=windows-1252"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://theserverside.com/customfacescomponents" prefix="cf"%>
<f:view>
  <html>
    <head>
      <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"></meta>
    </head>
    <body>
     <h:form>
           
     <p>A simple HelloWorld Custom Component:</p>
                        
     <p>The HelloWorld UI Component:</p>
     <cf:jsfhello hellomsg="Hello world! This is output from a custom JSF Component!" />
   
     </h:form>
    </body>
  </html>
</f:view>

Amikor fut az oldal a JSF saját komponensünk megjeleníti a "hellomsg" attribútumban definiált szöveget plusz az aktuális dátum, idő információkat. A továbbiakban megmutatom, milyen lépésekkel juthatunk el odáig, hogy így használhassuk a komponensünket!

UIComponent Class definiálása[szerkesztés]

A mi "HelloWorld" példánkban az UIComponent class a UIComponentBase class-ből származik. (Ami egyébként a JSF specifikációjában megtalálható.)

package tss.hello;

import java.util.Date;
import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;
import java.io.IOException;
import javax.faces.context.ResponseWriter;

public class HelloUIComp extends UIComponentBase {
  public void encodeBegin(FacesContext context) throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String hellomsg = (String)getAttributes().get("hellomsg");
   
    writer.startElement("h3", this);
    if(hellomsg != null) {
      writer.writeText(hellomsg, "hellomsg");
    } else {
      writer.writeText("Hello from a custom JSF UI Component!", null);
    }           
    writer.endElement("h3");   
    writer.startElement("p", this);
    writer.writeText(" Today is: " + new Date(), null);
    writer.endElement("p");   
  }

 public String getFamily() {
   return "HelloFamily";
 }
}

Ahogy azt láthatjuk ez az osztály megjelenít egy formázott "HelloWorld" üzenetet az encodeBegin() eljárással. Továbbá az is látható, hogy írtunk egy másik eljárást is, ez a getFamily(). Ez utóbbira azért van szükségünk mert az osztályt az UIComponentBase osztályból származtattuk. (A példánkban nem definiáltunk új családot, csupán egy string-gel tér vissza ez az eljárásunk.)

Encoding eljárások[szerkesztés]

Az UI-komponensek és/vagy a társított renderer osztályok encode*() eljárásokat használnak a megjelenítésre a kliens irányába. A fenti példa egy egyszerű HTML header tag-et (h3) használva megjeleníti a "hellomsg" attribútumban kapott szöveget, plusz hozzáfűz egy szintén egyszerű bekezdés tag-be (p) ágyazott dátumot. Az encodeBegin() eljárás jelen esetben megjeleníti a teljes tartalmat (nyitja és zárja is). Abban az esetben, ha UI-komponensünknek vannak leszármazott gyermek tag-jei vagy komponensei akkor az encodeChildren() eljárások fölüldefiniálásával fölül definiáljuk egyben az encodeEnd() éljárásokat is. Az encodeChildren() eljárások lehetővé teszik a gyermek komponenseknek, hogy meghívják az encodeEnd() eljárásokat és ezzel zárják a szülő tag-eket.

A saját UI-komponens beírása a Faces-config.xml fájlba[szerkesztés]

Mielőtt továbblépnénk a JSP tag handler és a TLD fájlokra, hozzá kell hogy adjunk egy új komponenst a Faces-config.xml fájlhoz, ami szükséges a komponensük működéséhez. A szintaxis az alábbiak szerint működik:

...
<faces-config xmlns="http://java.sun.com/JSF/Configuration">
  <component>
   <component-type>tss.hello.JsfHello</component-type>
   <component-class>tss.hello.HelloUIComp</component-class>
  </component>
   ...
</faces-config>

A "component-type" egy JSF által felismerhető név, amellyel a saját komponensünket azonosítjuk: "tss.hello.JsfHello" (A későbbiekben ezzel fogunk hivatkozni a komponensünkre a tag handler-ben) A "component-class" pedig az UI-komponensünk osztályának elérési útvonala (class path).

Építsünk egy Custom JSP Tag könyvtárat[szerkesztés]

A JSF komponens fejlesztésének folytatásához szükségünk lesz egy JSP tag handler osztályra amely a javx.faces.webapp.UIComponentTag osztályból származik. Ennek a fő célja, hogy összekapcsoljon egy JSP-ben is beágyazható tag-et az UI-komponenssel, továbbá hogy összekapcsoljon egy elkülönített renderer (megjelenítő) osztályt (ha szükséges) az UI-komponenssel. Illetve még az is, hogy beállítsa a változók értékeit a kapott tag attribútumok alapján.

Ez a mi tag handler osztályunk forrás kódja:

package tss.hello;

import javax.faces.application.Application;
import javax.faces.webapp.UIComponentTag;
import javax.faces.component.UIComponent;
import javax.faces.el.ValueBinding;
import javax.faces.context.FacesContext;

public class FacesHelloTag extends UIComponentTag {
  // Declare a bean property for the hellomsg attribute.
  public String hellomsg = null;

  // Associate the renderer and component type.
  public String getComponentType() { return "tss.hello.JsfHello"; }
  public String getRendererType() { return null; }

  protected void setProperties(UIComponent component) {
    super.setProperties(component);
    
    // set hellomsg
    if (hellomsg != null) { 
      if (isValueReference(hellomsg)) {
        FacesContext context = FacesContext.getCurrentInstance();
        Application app = context.getApplication();
        ValueBinding vb = app.createValueBinding(hellomsg);
        component.setValueBinding("hellomsg", vb);                  
      } else { 
        component.getAttributes().put("hellomsg", hellomsg);
      }
    }                         
  }

  public void release() {
    super.release();
    hellomsg = null;
  }

  public void setHellomsg(String hellomsg) {
    this.hellomsg = hellomsg;
  }

  public String getHellomsg() {
    return this.hellomsg;
  }
}

Először is létrehozunk egy String típusú változót (bean) a "hellomsg" attribútum számára (hellomsg bean property). Ehhez nyilván szükség van a getter/setter metódusokra, ezek az osztály alján helyezkednek el. (Ezek fontosak mert ezeken keresztül éri el a JSF a változó tartalmát!) A következő lépés az előre már beállított UI-komponens osztályunk megadása, illetve az elszeparált renderer osztály megadása. A mi példánkban csak az első van így a második metódus null-al tér vissza! A következő metódus a setProperties(), amely beállítja a hellomsg értékét. Bejövő adat, a JSP tag-ből, először meghívódik a setProperties() metódus. Figyeljük meg, hogy mi ezt a metódust felül definiáltuk és végzünk benne egy ellenőrzést az elején. Amennyiben a bejövő adat egy ValueReference, ami a html formból jön és JSF EL(Expression Language)kifejezés:#{Bean.Property}, akkor némileg más a logikája a programnak, hogy beállítsa az értéket. Továbbá a Tag Handler osztálynak szüksége van még egy metódusra, ez a release() metódus. Ez lényegében alaphelyzetbe állítja a bab (bean) tulajdonságait, változóit.

A Tag Könyvtárat leíró fájl felépítése(Tag Library Descriptor - tld)[szerkesztés]

Ahhoz, hogy használni tudjuk a custom JSP tag handler osztályunkat, szükséges azt beállítani egy tld fájlba, egy tag-ként aminek az attribútumai megadják az osztály főbb paramétereit. A szintaxist alábbi példa mutatja:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE taglib
  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" 
  "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
  <tlib-version>0.01</tlib-version>
  <jsp-version>1.2</jsp-version>
  <short-name>simple</short-name>
  <uri>http://theserverside.com/simplefacescomponents</uri>
  <description>This tag library contains simple JSF Component examples.</description>
  <tag>
    <name>jsfhello</name>
    <tag-class>tss.hello.FacesHelloTag</tag-class>
    <attribute> 
      <name>binding</name> 
      <description>A value binding that points to a bean property</description>
    </attribute> 
    <attribute> 
      <name>id</name> 
      <description>The client id of this component</description>
    </attribute> 
    <attribute> 
      <name>rendered</name>
      <description>Is this component rendered?</description>
    </attribute>
    <attribute> 
      <name>hellomsg</name>
      <description>a custom message for the Component</description>
    </attribute>
  </tag>
</taglib>

Követve a standard J2EE architektúrát a custom tld fájlt a /WEB-INF/ mappába kell helyeznünk!

Futtassuk a "HelloWorld" példát[szerkesztés]

Ahhoz, hogy lássuk az eredményt, le kell hogy fordítsuk az összes osztályt és tegyük őket bele a J2EE Web Module-ba. Ugyanis a rendszer itt fogja keresni őket. Továbbá adjuk hozzá a programunkhoz a szükséges jar file-okat(jsf-api.jar, jsf-impl.jar, commons-beanutils.jar, commons-collections.jar, commons-digester.jar, commons-logging.jar, jstl.jar, standard.jar[4]) (class-path helyes beállítása szükséges) Amennyiben a futtatási környezetet helyesen(runtime environment) állítjuk be tesztelhetjük is a saját JSF UI-komponensünket. Csak készítsünk egy JSF-t használó JSP-oldalt, és teszteljük.

Jegyzetek[szerkesztés]

Források[szerkesztés]