21 stycznia 2006

Uruchamiamy pierwszą aplikację w technologii JavaServer Faces

JavaServer Faces (JSF) jest technologią komponentów graficznych (innymi słowy: szkieletem programistycznym) do budowania aplikacji uruchomionych po stronie serwera aplikacyjnego. Poszczególne wersje specyfikacji JSF rozwijane są w procesie Java Community Process (JCP). Ostatnia finalna wersja specyfikacji rozwijana była jako JSR-252 i nazwana jako JavaServer Faces 1.2.

Jak podkreślają autorzy specyfikacji JSF 1.2, technologia JavaServer Faces oparta jest o następujące specyfikacje:
  • JavaServer Pages (JSP) 2.1
  • Java Servlet 2.5
  • Java SE 5.0
  • Java EE 5.0
  • JavaBeans 1.0.1
  • JavaServer Pages Standard Tag Library (JSTL) 1.2
i była tworzona z myślą o wykorzystaniu z innymi szkieletami programistycznymi, m.in. portletami (JSR-168).

Ideą przewodnią JSF było uproszczenie procesu tworzenia skomplikowanych aplikacji graficznych, których stan komponentów wizualnych uaktualniany jest automatycznie pomiędzy poszczególnymi akcjami użytkownika. Zbiór dostępnych komponentów graficznych pozwala na tworzenie aplikacji reagujących na zdarzenia wyzwalane po stronie klienta i przesyłanie ich, bez ingerencji programisty, na stronę serwera. Oprócz oczywistych zalet stosowania ustandaryzowanego szkieletu programistycznego z gotowymi komponentami do budowania interfejsu użytkownika, istnieje możliwość stworzenia i dystrybucji własnych. Przede wszystkim pozwala to na stworzenie narzędzi programistycznych, które dostarczałyby standardowych komponentów i dodatkowych rozszeżeń. Dołączając do nich możliwość tworzenia interfejsu graficznego poprzez technikę "przeciągnij-i-upuść" dochodzimy do przyczyn leżących u podstaw stworzenia technologii JavaServer Faces.

Istnienie specyfikacji poparte jest możliwością jej wykorzystania w projektach. Uruchomienie aplikacji opartej o JavaServer Faces wymaga środowiska uruchomieniowego, który dostarcza implementacji pomysłów opisanych w specyfikacji. Specyfikacja jest jedynie zbiorem wytycznych, podczas gdy implementacja pozwala na ich zmaterializowanie. Każdej specyfikacji w procesie Java Community Process (JCP) towarzyszy implementacja referencyjna (ang. RI - reference implementation). Implementacja referencyjna gwarantuje środowisko do uruchomienia aplikacji zgodnych z wytycznymi specyfikacji. Nie gwarantuje jednak jej szybkości działania i często nie dostarcza wielu dodatkowych uproszczeń, które są wielce porządane, jednakże ciało ustawodawcze nie dopracowało się jednolitego stanowiska odnośnie ich realizacji. W wielu przypadkach, zatem, powstają alternatywne implementacje specyfikacji. Ich zgodność stwierdzana jest poprzez uruchomienie tzw. TCK - Test Compatibility Kit, czyli zbioru testów do badania zgodności ze specyfikacją. W przypadku specyfikacji JavaServer Faces, alternatywną implementacją jest projekt wolnodostępny - Apache MyFaces.

Apache MyFaces (w skrócie MyFaces) jest wolnym oprogramowaniem tworzonym pod auspicjami Apache Software Foundation - niekomercyjnej fundacji rozwoju oprogramowania. Ostatnia wersja Apache MyFaces to 1.1.1.

Projekt składa się z dwóch części - podprojektów: biblioteki komponentów JSF oraz biblioteki rozszerzającej - Tomahawk.

Uruchomienie przykładowych aplikacji dostarczanych z Apache MyFaces

Projekt MyFaces rozprowadzany jest z wieloma przykładowymi aplikacjami, które prezentują możliwości JavaServer Faces w aplikacjach internetowych. Przykłady są rozprowadzane oddzielnie jako myfaces-wersja-examples, które można pobrać pod adresem http://myfaces.apache.org/binary.cgi. Do ich uruchomienia potrzebny jest kontener servletów, np. Apache Tomcat.

Wykonaj poniższe kroki w celu uruchomienia przykładowych aplikacji JSF projektu MyFaces na kontenerze servletów - Apache Tomcat.

  • Instalacja Apache Tomcat

    Instalacja Tomcata sprowadza się do rozpakowania odpowiedniej paczki do dowolnie wybranego katalogu (do którego odwołujemy się później za pomocą zmiennej CATALINA_HOME).

  • Kopiowanie przykładowych aplikacji

    Przekopiuj wszystkie pliki *.war z myfaces-wersja-examples do katalogu CATALINA_HOME/webapps

  • Uruchomienie aplikacji MyFaces

    Po uruchomieniu Tomcata (CATALINA_HOME/bin/catalina.bat run) pod adresem http://localhost:8080/simple znajduje się jedna z 4 aplikacji - simple. Zastąpienie simple w adresie przez blank, sandbox, czy tiles uruchamia pozostałe przykłady.

Zaleca się przejrzenie wszystkich przykładów, aby zapoznać się z możliwościami JSF w wydaniu Apache MyFaces.

Własna aplikacja JSF

Po zapoznaniu się z przykładowymi aplikacjami JSF dostarczanymi z MyFaces czas na stworzenie własnej. Nic tak nie uczy jak praktyka, a napotkane utrudnienia poprawiają naszą zdolność zapamiętywania. Dodatkowo, pozwala to w pełni dostrzec korzyści z potencjalnego wykorzystania we własnych projektach.
  • Utworzenie projektu aplikacji internetowej

    Możesz to wykonać w wybranym przez siebie środowisku IDE, bądź ręcznie zakładając katalog o nazwie mojapp w dowolnym miejscu.

  • Umieszczenie bibliotek MyFaces w aplikacji

    Biblioteki MyFaces znajdują się w katalogu głównym projektu MyFaces w pliku myfaces-all.jar. Przekopiuj plik do katalogu WEB-INF/lib aplikacji. Dodatkowo potrzebne nam bedą następujące biblioteki:

    W sumie potrzeba 12 plików jar w katalogu WEB-INF/lib.
  • Stworzenie deskryptora aplikacji internetowej - WEB-INF/web.xml

    Każda aplikacja internetowa wymaga pliku konfiguracyjnego - WEB-INF/web.xml. Deskryptor instalacji (ang. deployment descriptor) instruuje kontener servletów o wymaganych usługach. Zawiera on deklarację zewnętrznych zależności z kontenerem servletów. Znajdziemy tam m.in. deklarację servletów i ich mapowania na adresy URL. W naszej przykładowej aplikacji informacja o servlecie - Faces Servlet - i jego mapowaniu będzie wystarczająca.

    <?xml version="1.0" encoding="ISO-8859-2"?>
    <!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">
    <web-app>
    <description>Przykładowa aplikacja JSF</description>
    <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    </web-app>
    Powyższa konfiguracja zapewnia nam zainicjowanie kontekstu JSF poprzez wywołanie servletu javax.faces.webapp.FacesServlet.
    Mapowanie (poprzez servlet-mapping) zapewnia nam, że każde wywołanie typu *.jsf uruchomi FacesServlet, a ten zgodnie z wytycznymi w konfiguracji JSF - faces-config.xml - wykona odpowiednie czynności.

  • Stworzenie konfiguracji JSF - faces-config.xml

    Podobnie jak w przypadku aplikacji internetowych, które konfigurowane są poprzez swój własny deskryptor - web.xml - aplikacje oparte o JSF wymagają własnego pliku konfiguracyjnego. Standardowo jest to plik o nazwie faces-config.xml, jednakże nazwa podlega konfiguracji i można ją zmienić. Poniżej znajduje się plik konfiguracyjny dla naszej aplikacji.

    <?xml version="1.0" encoding="ISO-8859-2"?>
    <!DOCTYPE faces-config PUBLIC
    "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
    "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
    <faces-config>
    <application>
    <message-bundle>pl.org.laskowski.i18n.Komunikaty</message-bundle>
    <locale-config>
    <default-locale>pl</default-locale>
    <supported-locale>en</supported-locale>
    </locale-config>
    </application>
    <managed-bean>
    <managed-bean-name>formularz</managed-bean-name>
    <managed-bean-class>pl.org.laskowski.Formularz</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
    </managed-bean>
    <navigation-rule>
    <description>
    Kiedy dane formularza są poprawne wyświetl ekran powitalny,
    w przeciwnym przypadku wróć do formularza
    </description>
    <navigation-case>
    <from-outcome>poprawne_dane</from-outcome>
    <to-view-id>/witaj.jsp</to-view-id>
    </navigation-case>
    <navigation-case>
    <from-outcome>niepoprawne_dane</from-outcome>
    <to-view-id>/formularz.jsp</to-view-id>
    </navigation-case>
    </navigation-rule>
    </faces-config>
    Definiuje on plik zawierający komunikaty dopasowane do aktualnego języka, pojedyńczy komponent - formularz - odpowiedzialny za przechowywanie danych i dwie reguły nawigacyjne - poprawne_dane i niepoprawne_dane. Obie są jedynie nazwą dla docelowej strony jaka będzie wyświetlana, kiedy środowisko JSF otrzyma informację o przejściu z aktualnego widoku (strony) do odpowiadającemu nazwie widokowi.

  • Stworzenie pliku z komunikatami - Komunikaty_pl.properties

    Plik z komunikatami zawiera dane w postaci zgodnej z formatem wymaganym przez klasę java.util.Properties.

    wprowadz_imie = Wprowad\u017a swoje imi\u0119
    zatwierdz = Zatwierd\u017a
    witaj = Witaj
    ponownie = Jeszcze raz!
    tytul = Moja pierwsza aplikacja JSF

    javax.faces.component.UIInput.REQUIRED = Nie podano warto\u015bci
    Na uwagę zasługuje klucz javax.faces.component.UIInput.REQUIRED, który nadpisuje standardową wartość klucza w języku angielskim na polski odpowiednik. Pozostałe klucze są specyficzne dla aplikacji.

    Zapisz plik w katalogu WEB-INF/classes/pl/org/laskowski.

  • Utworzenie widoków (stron JSP) - formularz.jsp i witaj.jsp

    Strony (widoki) tworzące aplikację JSF są zwykłymi stronami JSP, które mogą zawierać specyficzne dla JSF znaczniki. W poniższych plikach wyróżnić można dwie główne biblioteki znaczników JSP - html i core. Obie dostarczają podstawowych komponentów wizualnych JSF.


    Pierwszy plik - formularz.jsp - prezentuje formularz, składający się z pojedyńczego pola tekstowego (h:inputText) i przycisku do zatwierdzenia formularza (h:commandButton). Można również zobaczyć wykorzystanie komunikatów z zewnętrznego pliku do ich lokalizacji (f:loadBundle).

    <%@ 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"%>
    <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>

    <html>
    <f:view locale="pl">
    <f:loadBundle basename="pl.org.laskowski.i18n.Komunikaty" var="komunikaty"/>

    <head>
    <title><h:outputText value="#{komunikaty.tytul}"/></title>
    </head>
    <body>
    <h:form id="formularzForm1">
    <h:outputText value="#{komunikaty['wprowadz_imie']}: "/>
    <h:inputText id="imie" value="#{formularz.imie}" maxlength="10" size="25" required="true"/>
    <h:message for="formularzForm1:imie" styleClass="error" />
    <br/>
    <h:commandButton value="#{komunikaty.zatwierdz}" action="#{formularz.submit}"/>
    </h:form>
    </body>
    </f:view>
    </html>
    Druga strona - witaj.jsp - wyświetla powitanie z wykorzystaniem wartości parametru podanej w formularzu (h:outputText) oraz umożliwia powrót do strony początkowej (h:outputLink).
    <%@ 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"%>
    <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%>

    <html>
    <body>
    <f:view>
    <f:loadBundle basename="pl.org.laskowski.i18n.Komunikaty" var="komunikaty"/>

    <h:outputText value="#{komunikaty['witaj']}"/>
    <h:outputText value="#{formularz.imie}"/>!
    <p/>
    <h:outputLink value="formularz.jsf" >
    <h:outputText value="#{komunikaty['ponownie']}"/>
    </h:outputLink>
    </f:view>
    </body>
    </html>

  • Utworzenie zarządzanego komponentu JSF - pl.org.laskowski.Formularz

    Stan pomiędzy widokami przechowywany jest w JSF przy pomocy komponentów zarządzanych (ang. managed beans). Nie jest to nic odkrywczego pamiętając, że podejście to jest wzorcem programistycznym - Model-View-Controller (MVC) - gdzie Modelem są właśnie klasy JavaBeans (nie mylić z Enterprise JavaBeans - EJB). Takie podejście znajdziemy w innych szkieletach programistycznych, które były prekursorem JavaServer Faces, np. Apache Struts.

    Komponenty zarządzane są klasami z metodami dostępowymi zgodnymi ze specyfikacją JavaBeans. Nie ma wymagania odnośnie implementacji klasy poza zgodnością ze specyfikacją JavaBeans, która, m.in. nakreśla sposób dostępu do danych za pomocą metod set (ang. setters) i get (ang. getters).

    package pl.org.laskowski;

    public class Formularz {

    private String imie;

    public String getImie() {
    return imie;
    }

    public void setImie(String email) {
    this.imie = email;
    }

    public String submit() {
    if ("Jacek".equals(getImie())) {
    return "poprawne_dane";
    }
    return "niepoprawne_dane";
    }
    }

    Skompilowana klasa pl.org.laskowski.Formularz powinna znajdować się w katalogu WEB-INF/classes/pl/org/laskowski.

  • Początkowy plik aplikacji - index.jsp

    Uruchomienie aplikacji internetowej w środowisku kontenera servletów przez użytkownika wiąże się z uruchomieniem pewnego servletu (JSP jest również servletem). Standardowo uruchamianą stroną w kontenerze servletów jest m.in. index.jsp. Ważnym krokiem zapewniającym uruchomienie strony startowej jest jej istnienie. Nie można zdefiniować strony startowej w deskryptorze aplikacji, jeśli plik nie istnieje fizycznie w aplikacji. Drugą ważną rzeczą jest, aby nasze strony, zawierające znaczniki JSF, były wywoływane przez FacesServlet, który zapewni poprawną konfigurację środowiska JSF. Z tych dwóch powodów konieczne jest posłużenie się pewnym rozwiązaniem - stroną JSP, która zostanie zadeklarowana w web.xml jako strona startowa aplikacji i która będzie przekierowywała użytkownika na właściwą stronę JSF - w naszym przypadku formularz.jsf.

    <%
    response.sendRedirect("formularz.jsf");
    %>
  • Uruchomienie aplikacji

    Po stworzeniu wszystkich w/w plików najwyższa pora zobaczyć naszą aplikację w akcji. Przekopiuj katalog mojapp do katalogu CATALINA_HOME/webapps i uruchom Tomcata. Pod adresem http://localhost:8080/mojapp znajduje się nasza aplikacja.

    Komentarze, spostrzeżenia mile widziane. Pomysły na następne artykuły również. Skorzystaj z możliwości komentowania artykułu, poniżej.

6 komentarzy:

  1. Cześć!

    Jeśli ktoś dopiero zaczyna zabawę z JSF chciałbym zwrócić uwagę na to, że wszelkie komponenty JSF należy umieszczać między znacznikami f:view lub f:subview

    Pozdrawiam
    GW

    OdpowiedzUsuń
  2. Racja. Często zastanawiam się jak nisko/głęboko zejść omawiając daną technologię, aby nie zaciemnić tematu i przedstawić go w maksymalnie uproszczonym i zrozumiałym języku. Może powinienem przygotować coś na kształt "Uruchamiany ToIOwo - Krok po Kroku".

    OdpowiedzUsuń
  3. Przykłady aplikacji jsf można również znaleźć na stronie:
    http://www.irian.at/myfaces.jsf

    OdpowiedzUsuń
  4. Trzeba jeszcze zwrocic uwage na wg mnie dziwne przekierowania.
    Tworzymy plik *.jsp, ale przekierowujemy do pliku *.jsf. Plik jsf moze nie istniec, ale musi istniec plik *jsp, bo koniec końców to on jest przetwarzany.

    OdpowiedzUsuń
  5. Gratuluje poradnika, jednak mam pewien niauns, z którym się męczę własnie - chodzi o własne opisy błędów, o ile "javax.faces.component.UIInput.REQUIRED=jakies przypisanie" działa poprawnie to wszelkie moje proby nadpisania org.apache.myfaces.Email.INVALID_detail spełzaja na niczym. Komunikat ten powinien dotyczyć, jak myslę walidacji email także w Tomahawk'owym <t:validateEmail /> - czyżby jeszcze jakies inne bundle do tego były?

    OdpowiedzUsuń
  6. Witam

    Miałem przyjemność uczestniczyć w paru spotkaniach grupy plbug podczas mojej delegacji w wawie. Miałem na nich okazje posłuchać o JSF i mnie zachęciłeś do użycia tego. Niestety delegacja się skończyła a zaczęły się problemy z JSF :/. Mam kilka pytań na które nie mogę znaleźć odpowiedzi może coś poradzicie.
    1. W strutsach mamy możliwość zmapowania akcji na url, czyli wpisując urla wywołujemy akcje a potem wyświetla się strona. Jak zrobić takie coś w JSF? Chodzi o to żeby przed wyświetleniem strony wykonała się jakaś akcja np wydobycie danych z db.
    2. Czy wszystko co znajduje się na stronie jsp musi być komponentem JSF. Przecież jak do aplikacji usiądzie grafik to się załamie, jeżeli nie będą mu się renderować obrazki itp.

    OdpowiedzUsuń