home_site

Lab02 - Jakarta Server Faces [ ver. ZTI.2025.03.23.003 ]

Zawartość strony

Zadania realizowane w ramach laboratorium

W projekcie zostanie opracowany serwis WWW zgodny z ideą Jakarta Server Faces 4.0.0 z dostępem do bazy danych poprzez interfejs JDBC. W ramach projektu zapoznamy się z ideą tworzenia szablonu strony, walidacją danych wprowadzanych poprzez formularz oraz komponentami zarządzanymi w technologii JSF. Na koniec opracowany zostanie skrypt umożliwiający filtrowanie żądań obsługiwanych przez serwis.

Skrypty opracowane w ramach zajęć.

  1. Środowisko uruchomieniowe, wymagane biblioteki
  2. Pierwszy skrypt w JSF - przygotowanie projektu
  3. Utworzenie szablonu JSF do realizacji projektu
  4. Formularz do odczytu danych z bazy danych i odpowiedni widok w JSF
  5. Formularz dodawanie rekordów do bazy danych
  6. Formularz odczytu pojedynczego rekordu z bazy danych
  7. Formularz do poprawy rekordów w bazie danych
  8. Skrypty usuwające rekord z bazy danych
  9. Ustawienie aplikacji dla różnych języków
  10. Skrypty do filtrowanie żądań w ramach aplikacji
  1. Środowisko uruchomieniowe, wymagane biblioteki

    Projekt zostanie uruchomieny z wykorzystaniem technologii JSF na serwerze aplikacyjnym TomEE. Od wersji 2.3 JSF zawiera technologię CDI (Contexts and Dependency Incjection) do wspierania komponentów zgodnie z tą technologią i wymaga do wdrożenia pełnej obsługi technologii JEE (Jakarta EE). Technologia CDI nie jest wspierana przez standardową wersję Tomcat'a. Obsługa komponentów zarządzanych na poziomie serwletów ( kontener Tomcat ) dostępna w wersjach wcześniejszach została wycofana z najnowszych wersji biblioteki JSF.

    W ramach prezentowanego projektu wykorzystamy bibliotekę Jakarta Servwer Faces w wersji 4.0, która jest dostępna w ramach technologii Jakarta EE w wersji 10. Implementacja Jakarta Faces dostępna jest w Jakarta EE 10 oraz implementacji Mojarra.

  2. Pierwszy skrypt w JSF - przygotowanie projektu

    1. W środowisku IDEA tworzymy nowy projekt ZTI_lab02 oraz wybieramy w zależnościach "Full Platform" ( rys.1 i rys.2).
      Lab02_JSF_conf1
      Rys.1 Konfiguracja projektu zrealizowanego w technologii JSF
      Lab02_JSF_conf2
      Rys.2 Ustawienie projektu - wybór zależności w projekcie
    2. Po wygenerowaniu projektu modyfikujemy plik web.xml zgodnie z poniższym wzorcem.
           <servlet>
          <servlet-name>Faces Servlet</servlet-name>
          <servlet-class>jakarta.faces.webapp.FacesServlet</servlet-class>
        </servlet>
        
        <servlet-mapping>
          <servlet-name>Faces Servlet</servlet-name>
          <url-pattern>*.jsf</url-pattern>
        </servlet-mapping>
        
        <welcome-file-list>
          <welcome-file>index.jsf</welcome-file>
          <welcome-file>welcome.jsf</welcome-file>
          <welcome-file>index.html</welcome-file>
          <welcome-file>index.jsp</welcome-file>
        </welcome-file-list>
        
        
        <context-param>
          <!--description>
              The location where state information is saved. Valid values
              are 'server' (typically saved in HttpSession) and 'client'
              (typically saved as a hidden field in the form. Default is
              server.
              </description-->
          <param-name>jakarta.faces.STATE_SAVING_METHOD</param-name>
          <param-value>client</param-value>
        </context-param>
      
    3. W celu sprawdzenia poprawności działania projektu tworzymy plik test.xhtml ( korzystając z new facelets ) w którym wstawiamy poniższy tekst. Plik zostanie utworzony w głównym katalogu projektu webapp.

      Skrypt test.xhtml - ( [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
            xmlns:h="http://xmlns.jcp.org/jsf/html" >
      
      <h:head>
          <title>Facelet Title</title>
      </h:head>
      <h:body>
          Hello from Facelets
      </h:body>
      
      </html>
      
    4. Poprawność działania skryptu sprawdzamy w przeglądarce ( adres URL jest zależny od ustawień projektu i serwera TomEE ) http://localhost:8080/zti_lab02 ... /test.jsf. (rys.3)
      Lab02_test_jsf
      Rys.3 Strona Test.jsf w przeglądarce
    5. Sprawdźmy kod źródłowy przesłanej strony z serwera do przeglądarki. W tym celu należy wyświetlić stronę w zewnętrznej przeglądarce, która udostępnia powyższą funkcjonalność pod klawiszem F12. Proszę przeanalizować ich zawartość. Na rys.4 przedstawiona jest żródło strony http://localhost:8080/zti_lab02 ... /test.jsf, natomiast na rys.5 żródło strony http://localhost:8080/zti_lab02 ... /test.xhtml. Strona test.xhtml zawiera kod źródłowy JSF bez jego sparsowania. W końcowym punkcie projektu zrealizujemy blokadę dostępu do kodu źródłowego aplikacji - skryptów *.xhtml. tzw. facelets.
      Lab02_source_jsf
      Rys.4 Źródło strony - test.jsf
      Lab02_source_xhtml
      Rys.5 Źródło strony - test.xhtml
  3. Utworzenie szablonu do realizacji projektu w JSF

    1. W ramach zajęć opracujemy projekt z użyciem tzw. faceletów wykorzystując możliwość generowania stron na podstawie szablonów. Do realizacji zadań w trakcie przetwarzania szablonów wykorzystywane są specjalne znaczniki JSF. Poniżej krótka chrakterystyka wykorzystanych w projekcie znaczników.
      ui:insert
      Wstawia treść do szablonu zdefiniowanego w ramach znacznika ładującego szablon.
      ui:include
      Dołącza treść z innego pliku w formacie XML.
      ui:composition
      Użyty bez atrybutu template reprezentuje sekwencję elementów, które można wstawić w innym miejscu. Użyty z atrybutem template powoduje załadowanie szablonu. Znaczniki potomne definiują elementy zmienne tego szablonu.
      ui:define
      Definiuje treść wstawianą do szablonu zawierającego odpowiedni znacznik ui:insert
    2. Na początek tworzymy w katalogu "webapp" podkatalog "templates" zawierający poniższy plik szablonu tworzonego serwisu WWW - masterTemplate.xhtml.

      Szablon masterTemplate.xhtml - ( [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
            xmlns:h="http://xmlns.jcp.org/jsf/html"
            xmlns:f="http://xmlns.jcp.org/jsf/core"
            xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
      
      <h:head>
        <title><ui:insert name="windowTitle"></ui:insert></title>
        <h:outputStylesheet library="css" name="style.css" />
      </h:head>
      
      <h:body>
        <!--ui:debug hotkey="x" rendered="#{initParam['javax.faces.FACELETS_DEVELOPMENT']}"/-->
      
        <div id="header">
          <ui:insert name="header">
            <!--  include your header file or uncomment the include below and create header.xhtml in this directory -->
            <ui:include src="/section/main/header.xhtml"/>
          </ui:insert>
        </div>
      
      
        <div id="content">
          <ui:insert name="content">
            <!--  include your content file or uncomment the include below and create content.xhtml in this directory -->
            <!-- <div> -->
            <!-- <ui:include src="content.xhtml"/> -->
            <!-- </div> -->
          </ui:insert>
        </div>
      
        <div id="footer">
          <ui:insert name="footer">
            <!--  include your header file or uncomment the include below and create footer.xhtml in this directory -->
            <ui:include src="/section/main/footer.xhtml"/>
          </ui:insert>
        </div>
      
      </h:body>
      
      </html>
      
    3. W kolejnym kroku tworzymy w katalogu "webapp" katalog "section" w którym tworzymy katalog "main". W utworzonym katalogu "main" umieszczamy dwa pliki "header.xhtml" i "footer.xhtml".

      Skrypt header.xhtml - ( [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core">
      <head>
          <title>IGNORED</title>
      </head>
      
      <body>
      <ui:composition>
          <div class="header">
              <h1>
                  #{msg.applTitle}
              </h1>
              <h:form>
                  <!-- h:link value="Home"  id="index"  outcome="index" /-->
                  <!-- h:link value="Lista" id="list"  outcome="list"/-->
                  <!-- h:link value="Dodaj" id="add"   outcome="add"/-->
                  <h:commandButton value="#{msg.linkHome}" id="home" action="index?faces-redirect=true" />
                  <h:commandButton value="#{msg.linkList}" id="list" action="list?faces-redirect=true" />
                  <h:commandButton value="#{msg.linkAdd}" id="add" action="add?faces-redirect=true" />
              </h:form>
      
          </div>
      </ui:composition>
      </body>
      </html>
      

      Szablon footer.xhtml - ( [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core">
      <head>
          <title>IGNORED</title>
      </head>
      <body>
      <ui:composition>
          <div class="footer">
              #{msg.footer}
          </div>
      </ui:composition>
      </body>
      </html>
      
    4. Na koniec tworzymy główną stronę projektu zawartą w pliku index.xhtml bezpośrednio w katalogu "webapp".

      Skrypt index.xhtml - ( [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
            xmlns:h="http://xmlns.jcp.org/jsf/html"
            xmlns:f="http://xmlns.jcp.org/jsf/core"
            xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
      <head>
          <title>IGNORED</title>
      </head>
      
      <body>
      <ui:composition template="/templates/masterTemplate.xhtml" >
          <ui:define name="windowTitle">
              #{msg.siteTitle}
          </ui:define>
      
      
          <ui:define name="content">
              #{msg.welcomePage}
          </ui:define>
      
      </ui:composition>
      </body>
      
      </html>
      
    5. Do poprawnego działania projektu wymagane jest jeszcze utworzenie pliku messages.properties zawierającego opis pól wykorzystywanych w ramach skryptów JSF w wyrażeniach #{msg.siteTitle} i #{msg.welcomePage} oraz utworzenie pliku deskryptora JSF faces-config.xml w katalogu WEB-INF z odpowiednim wpisem. Plik messages.properties tworzymy w katalogu "data" który tworzymy w katalogu "resources" projektu.
      Poniżej przykładowa zawarość pliku messages.properties.
      siteTitle=ZTI Lab02 JSF TomEE
      welcomePage=Prezentacja technologii JSF
      applTitle=Baza uzytkownikow serwisu
      linkHome=Home
      linkList=Lista
      linkAdd=Dodaj
      footer=...: ZTI 2025, Lab 02 - JSF - Apache TomEE :...
      
      Tworzymy plik faces-config.xml ( New -> XML Configuration file -> JSF Config ) z poniższą zawartością.
      <?xml version='1.0' encoding='UTF-8'?>
      <faces-config version="4.0" xmlns="https://jakarta.ee/xml/ns/jakartaee"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                    xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                                        https://jakarta.ee/xml/ns/jakartaee/web-facesconfig_4_0.xsd" >
      
          <application>
              <resource-bundle>
                  <base-name>data.messages</base-name>
                  <var>msg</var>
              </resource-bundle>
          </application>
      
      </faces-config>
    6. Na rys.6 przedstawiona została struktura plików w tworzonym projekcie.
      Lab02_proj_tree_1
      Rys.6 Struktura plików w tworzonym projekcie.
    7. Poprawność tworzonego projektu sprawdzamy w przeglądarce - http://localhost:8080/zti_la02n ... / (rys.7).
      Lab02_proj_appl_1
      Rys.7 Wygląd realizowanego projektu - http://localhost:8080/zti_lab02n ... /
  4. Odczyt danych z bazy danych i odpowiedni widok w JSF

    1. W kolejnym punkcie zrealizujemy podłączenie do bazy danych. W tym celu wykorzystamy bazę danych PostgreSQL, która została utworzona na poprzednich zajęciach. Do tabeli person dodajemy dodatkowe pola email i tel.
      -- Przykładowe polecenie SQL
      ALTER TABLE person ADD email varchar(50);
      ALTER TABLE person ADD tel varchar(50);
      
      Na koniec dodajemy do projektu zależność udostępniająca w projekcie sterownik bazy danych.
      <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.5.4</version>
      </dependency>
      
    2. W ramach tego punktu poznamy następujące komponenty tworzące interfejs JSF. Znaczniki posiadające przedrostek h: należą do grupy tworzącej elementy w kodzie HTML. Pisząc skrypt w JSF jako faclet (projekt facelets wprowadzony w JSF wersji 2.0 realizowany w ramach plików *.xhtml a nie skryptów JSP) wszytkie znaczniki oferujące funkcjonalność języka HTML poprzedzamy przedrostkiem h: (np. zamiast <head> piszemy <h:head> ). Natomiast znaczniki posiadające przedrostek f: realizują funkcjonalność dostępną w ramach JSF, która jest niezależna od języka HTML.
      h:form
      wizualizuje formularz HTML
      h:dataTable
      kontrolka tabeli
      h:column
      kolumna w tabeli dataTable
      h:outputText
      jednowierszowe wyjściowepole tekstowe
      h:commandButton
      przycisk akceptacji, czyszczenia formularza lub opcji
      f:facet
      dodaje facetę do komponentu
      f:verbatim
      przekształca tekst zawierający znacznik w komponent
    3. Na początek zmodyfikujemy plik nagłówkowy header.xhtml naszego projektu dodając przyciski nawigacyjne, do których następnie będziemy dodawać odpowiednią funkcjonalność.
      <h:form>
      <!-- h:link value="Home"  id="index"  outcome="index" /-->
      <!-- h:link value="Lista" id="list"  outcome="list"/-->	
      <!-- h:link value="Dodaj" id="add"   outcome="add"/-->
      <h:commandButton value="#{msg.linkHome}" id="home" action="index?faces-redirect=true" />
      <h:commandButton value="#{msg.linkList}" id="list" action="list?faces-redirect=true" />
      <h:commandButton value="#{msg.linkAdd}" id="add" action="add?faces-redirect=true" /> 
      </h:form>
      
    4. Formularz, który będzie wyświetlał zawartość pobraną z bazy danych umieścimy w skrypcie list.xhtml w katalogu "webapp" przedstawionym poniżej.

      Skrypt list.xhtml - ( [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
            xmlns:h="http://xmlns.jcp.org/jsf/html"
            xmlns:f="http://xmlns.jcp.org/jsf/core"
            xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
      <head>
          <title>IGNORED</title>
      </head>
      
      <body>
      <ui:composition template="/templates/masterTemplate.xhtml">
          <ui:define name="windowTitle">
              #{msg.siteTitle}
          </ui:define>
      
      
          <ui:define name="content">
              <div id="content">
                  #{msg.titleFormList}
                  <h:form>
                      <h:dataTable value="#{personBean.people}" var="record" border="1">
                          <h:column>
                              <!-- kolumna zawiera nazwiska osob -->
                              <f:facet name="header">
                                  <h:outputText value="#{msg.Lastname}" />
                              </f:facet>
                              <h:outputText value="#{record.lname}" />
                          </h:column>
                          <h:column>
                              <!-- kolumna zawiera imiona osob -->
                              <f:facet name="header">
                                  <h:outputText value="#{msg.Firstname}" />
                              </f:facet>
                              <h:outputText value="#{record.fname}" />
                          </h:column>
                          <h:column>
                              <!-- kolumna zawiera e-mail osob -->
                              <f:facet name="header">
                                  <h:outputText value="#{msg.Email}" />
                              </f:facet>
                              <h:outputText value="#{record.email}" />
                          </h:column>
      
                      </h:dataTable>
                  </h:form>
              </div>
          </ui:define>
      
      </ui:composition>
      
      
      </body>
      
      
      </html>
      
    5. W ramach projektu dane z bazy danych będą pobierane przez dwie klasy udostępniane przez mechanizm CDI. Dostęp do bazy danych zrealizowany został w oparciu o klasę Javy Database.java w aplikacji klasa dostępna poprzez databaseBean oraz klasę Javy dostarczającą dane z tabeli Person.java dostępną w aplikacji poprzez nazwę personBean.
      Klasy zostały umieszczamy w pakiecie "zti.model". W ramach klasy database należy wpisać poprawnie parametry połączenia z bazą danych: host, database name, user name i password.

      Klasa Person.java - ( [listing dokumentu] [link do dokumentu]

      package zti.model;
      
      import java.util.List;
      
      import jakarta.faces.component.UIParameter;
      import jakarta.faces.event.ActionEvent;
      
      import jakarta.inject.Named;
      import jakarta.enterprise.context.RequestScoped;
      
      @Named ("personBean")
      @RequestScoped
      public class Person {
      
          private String lname ;
          private String fname ;
          private String city ;
          private String email ;
          private String tel ;
          private Integer id;
      
          public Person() {}
      
          public Person(String lname, String fname, String city, String email, String tel, Integer id){
              this.lname = lname;
              this.fname = fname;
              this.city = city;
              this.email = email;
              this.tel = tel;
              this.id = id;
          }
      
          public void setPerson(Person person){
              this.setLname(person.getLname());
              this.setFname(person.getFname());
              this.setCity(person.getCity());
              this.setEmail(person.getEmail());
              this.setTel(person.getTel());
              this.setId(person.getId());
          }
      
          public Person getPerson(){
              return new Person(this.getLname(),
                      this.getFname(),
                      this.getCity(),
                      this.getEmail(),
                      this.getTel(),
                      this.getId() );
          }
      
          public void setFname( String newValue ){ fname = newValue ; }
          public String getFname() { return fname ; }
          public void setLname ( String newValue ) { lname = newValue ; }
          public String getLname() { return lname ; }
          public void setCity ( String newValue ) { city = newValue ; }
          public String getCity() { return city ; }
          public void setEmail ( String newValue ) { email = newValue ; }
          public String getEmail() { return email ; }
          public void setTel ( String newValue ) { tel = newValue ; }
          public String getTel() { return tel ; }
          public void setId ( Integer newValue ) { id = newValue ; }
          public Integer getId() { return id ; }
      
      
      
          public List<Person> getPeople()  {
              Database baza = new Database() ;
              List<Person> people = baza.getPersonList();
              return people ;
          }
      
      
      }
      

      Klasa Database.java - ( [listing dokumentu] [link do dokumentu]

      package zti.model;
      
      import java.sql.Connection;
      import java.sql.DriverManager;
      import java.sql.PreparedStatement;
      import java.sql.ResultSet;
      import java.sql.SQLException;
      import java.sql.Statement;
      import java.util.ArrayList;
      import java.util.List;
      
      import jakarta.enterprise.context.RequestScoped;
      import jakarta.inject.Named;
      
      @Named("databaseBean")
      @RequestScoped
      public class Database {
      
          private Person person;
          private Connection conn = null;
          private PreparedStatement prestmt = null;
          private Statement stmt = null;
          private ResultSet rset = null;
          private List<Person> list = new ArrayList<Person>();
      
          public Database() {
              // System.out.println("Init managed Bean");
              try {
                  Class.forName("org.postgresql.Driver");
              } catch (Exception ex) {
                  System.out.println(ex.getMessage());
              }
              String url = "jdbc:postgresql://qdjjtnkv.db.elephantsql.com:5432/<<database>>";
              String username = "<<user>>";
              String password = "<<pass>>";
              try {
                  conn = DriverManager.getConnection(url, username, password);
                  // System.out.println("Connect to Database");
              } catch (SQLException ex) {
                  System.out.println(ex.getMessage());
              }
          }
      
          public List<Person> getPersonList() {
              try {
                  stmt = conn.createStatement();
                  String sql = "SELECT * FROM public.person ORDER BY lname";
                  rset = stmt.executeQuery(sql);
                  while (rset.next()) {
                      person = new Person();
                      person.setFname(rset.getString("fname"));
                      person.setLname(rset.getString("lname"));
                      person.setEmail(rset.getString("email"));
                      person.setTel(rset.getString("tel"));
                      person.setCity(rset.getString("city"));
                      person.setId(rset.getInt("id"));
                      list.add(person);
                  }
                  rset.close();
              } catch (SQLException ex) {
                  System.out.println(ex.getMessage());
              } finally {
                  try {
                      if (prestmt != null) {
                          prestmt.close();
                      }
                      if (conn != null) {
                          conn.close();
                      }
                  } catch (SQLException ex) {
                      System.out.println(ex.getMessage());
                  }
              }
              return list;
          }
      
      }
      
      
    6. Do poprawnego działania aplikacji wymagane jest jeszcze uzupełnienie pliku messages.properties o odpowiednie wpisy (titleFormList, Lastname, Firstname, Email).
    7. Sprawdzamy poprawność działania aplikacji wybierając przycisk "Lista" (rys.8).
      Lab02_view_appl_2
      Rys.8 Strona prezentująca dane z bazy danych - przycisk Lista.
  5. Dodawanie rekordów do bazy danych

    1. Na początek opracujemy formularz add.xhtml (w katalogu "webapp") wprowadzający dane do bazy danych. W formularzu wykorzystane zostały nasstępujące znaczniki:
      h:panelGrid
      układ tabelaryczny
      h:outputLabel
      etykieta innego komponentu
      h:inputText
      kontrolka jedno wierszowego pola tekstowego
      h:message
      wyświetla komunikat dla komponentu

      W ramach kontroli wprowadzonych danych wykorzystamy atrybut required, który w przypadku braku danych wyświetla odpowiedni komunikat. Komunikaty zostały umieszczone w pliku mess-errors.properties. Komunikat o błędzie jest wyświetlany w polu <h:message>.

      Skrypt add.xhtml - ( [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core" >
      <head>
          <title>IGNORED</title>
      </head>
      
      <body>
      <ui:composition template="/templates/masterTemplate.xhtml">
          <ui:define name="windowTitle">
              #{msg.siteTitle}
          </ui:define>
      
          <ui:define name="content">
              <div id="content">
                  #{msg.titleFormAdd}
      
                  <h:form>
                      <h:panelGrid columns="3"  frame="hsides" cellspacing="3" >
                          <h:outputLabel for="fname" value="#{msg.Firstname}" />
                          <h:inputText id="fname" value="#{personBean.fname}"
                                       required="true"   requiredMessage="#{errors.AddFirstname}" />
                          <h:message for="fname" />
                          <h:outputLabel for="lname" value="#{msg.Lastname}" />
                          <h:inputText id="lname" value="#{personBean.lname}"
                                       required="true"   requiredMessage="#{errors.AddLastname}" />
                          <h:message for="lname" />
                          <h:outputLabel for="city" value="#{msg.City}" />
                          <h:inputText id="city" value="#{personBean.city}" />
                          <h:message for="city" />
                          <h:outputLabel for="email" value="#{msg.Email}" />
                          <h:inputText id="email" value="#{personBean.email}"
                                       required="true"   requiredMessage="#{errors.AddEmail}" >
                          </h:inputText>
                          <h:message for="email" />
                          <h:outputLabel for="tel" value="#{msg.Tel}" />
                          <h:inputText id="tel" value="#{personBean.tel}" />
                          <h:message for="tel" />
                      </h:panelGrid>
                      <h:commandButton value="#{msg.saveLinkFormAdd}" action="list"  actionListener="#{personBean.savePerson}"  id="save"   />
      
                  </h:form>
      
              </div>
      
          </ui:define>
      
      </ui:composition>
      
      
      </body>
      
      </html>
      
    2. Uzupełniamy zawartość pliku messages.properties o dodatkowe klucze wykorzystywane w szablonie formularza.
      welcomePage=Prezentacja technologii JSF
      Firstname=Imie
      Lastname=Nazwisko
      City=Miasto
      Email=E-mail
      Tel=Telefon
      
      Komunikaty błędów zwracane przez formularz umieścimy w pliku mess-errors.properties, który umieszczamy w tym samym katalogu co plik messages.properties. Poniżej przykładowe komunikaty.
      AddFirstname=Podanie imienia jest wymagane
      AddLastname=Podanie nazwiska jest wymagane
      AddEmail=Podanie e-mail jest wymagane
      
      W pliku faces-config.xml dodajemy wskazanie na plik zawierający komunikaty o błędach w ramach znacznika <application>.
      		<resource-bundle>
      			<base-name>data.mess-errors</base-name>
      			<var>errors</var>
      		</resource-bundle>
      
    3. W kolejnym kroku uzupełnimy zawartość klas Database.java i Person.java o funkcjonalność wstawiania rekordu do bazy danych.
      Klasę Database.java uzupełniamy o metodę createPerson().
      	public void createPerson(Person person) {
      		try {
      			prestmt = conn.prepareStatement("INSERT INTO person (fname, lname, email, city, tel) VALUES (?,?,?,?, ?)");
      			prestmt.setString(1, person.getFname());
      			prestmt.setString(2, person.getLname());
      			prestmt.setString(3, person.getEmail());
      			prestmt.setString(4, person.getCity());
      			prestmt.setString(5, person.getTel());
      			prestmt.executeUpdate();
      		} catch (SQLException ex) {
      			System.out.println(ex.getMessage());
      		} finally {
      			try {
      				if (prestmt != null) {
      					prestmt.close();
      				}
      				if (conn != null) {
      					conn.close();
      				}
      			} catch (SQLException ex) {
      				System.out.println(ex.getMessage());
      			}
      		}
      		return;
      	}
      
      Klasę Person.java uzupełnimy o metodę savePerson().
        public void savePerson(ActionEvent event)  {
            Database baza = new Database() ;
            baza.createPerson ( this.getPerson() ) ;
            return ;
         }
      
    4. Sprawdzamy poprawność działania aplikacji, na rys.9 przedstawiono ekran formularza wprowadzania danych z odpowiednimi komunikatami.
      Lab02_view_appl_3
      Rys.9 Formularz wprowadzania danych.
  6. Odczyt pełnego rekordu z bazy danych

    1. Na początek uzupełnimy formularz list.xhtml wyświetlający rekordy zapisane w bazie danych o dodatkowe funkcje działające dla wybranego rekordu:
      • wyświetlenie wszystkich atrybutów zawartych w bazie danych,
      • modyfikacja rekordu,
      • usunięcie rekordu z bazy danych.

      Uzupełnienie skryptu list.xhtml - ( [listing dokumentu] [link do dokumentu]

      			<h:column>
      				<f:facet name="header">
      					<h:outputText value="#{msg.actionFormList}" />
      				</f:facet>
      			[<h:commandLink id="View" action="view"
      						actionListener="#{personBean.selectPerson}">
      					<h:outputText value="#{msg.viewLinkFormList}" />
      						<f:param id="viewId" name="id" value="#{record.id}" />
      					</h:commandLink>]						
      			[<h:commandLink id="Edit" action="update"
      						actionListener="#{personBean.selectPerson}">
      					<h:outputText value="#{msg.editLinkFormList}" />
      						<f:param id="editId" name="id" value="#{record.id}" />
      					</h:commandLink>]
      			[<h:commandLink id="Delete" action="list"
      						actionListener="#{personBean.deletePerson}">
      					<h:outputText value="#{msg.delLinkFormList}" />
      						<f:param id="deleteId" name="id" value="#{record.id}" />
      					</h:commandLink>]
      			</h:column>
      Uwaga, powyższy kod należy dodać w ramach elementu <h:dataTable> jako ostatnie pozycje.
    2. Formularz, który będzie wyświetlał zawartość pobraną z bazy danych umieścimy w skrypcie view.xhtml przedstawionym poniżej. Plik umiesczamy w katalogu "webapp".

      Skrypt view.xhtml - ( [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
            xmlns:h="http://xmlns.jcp.org/jsf/html"
            xmlns:f="http://xmlns.jcp.org/jsf/core"
            xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
      <head>
          <title>IGNORED</title>
      </head>
      
      <body>
      <ui:composition template="/templates/masterTemplate.xhtml">
          <ui:define name="windowTitle">
              #{msg.siteTitle}
          </ui:define>
          <ui:define name="content">
              <div id="content">
                  #{msg.titleFormView}
                  <h:form>
                      <h:panelGrid columns="2" frame="hsides" cellspacing="2">
                          <h:outputLabel for="fname" value="#{msg.Firstname}" />
                          <h:outputText id="fname" value="#{personBean.fname}" />
                          <h:outputLabel for="lname" value="#{msg.Lastname}" />
                          <h:outputText id="lname" value="#{personBean.lname}" />
                          <h:outputLabel for="city" value="#{msg.City}" />
                          <h:outputText id="city" value="#{personBean.city}" />
                          <h:outputLabel for="email" value="#{msg.Email}" />
                          <h:outputText id="email" value="#{personBean.email}" />
                          <h:outputLabel for="tel" value="#{msg.Tel}" />
                          <h:outputText id="tel" value="#{personBean.tel}" />
                      </h:panelGrid>
      
                  </h:form>
              </div>
          </ui:define>
      </ui:composition>
      </body>
      </html>
    3. Na koniec uzupełniamy zawartość klas Database.java i Person.java o funkcjonalność wyświetlenia pełnego rekordu do bazy danych.
      Klasę Database.java uzupełniamy o metodę readPerson().
      	public Person readPerson(String id) {
      		try {
      			Integer ident = Integer.parseInt(id);
      			prestmt = conn.prepareStatement("SELECT * FROM person WHERE id = ? ");
      			prestmt.setInt(1, ident);
      			ResultSet rset = prestmt.executeQuery();
      			person = new Person();
      			while (rset.next()) {
      				person.setFname(rset.getString("fname"));
      				person.setLname(rset.getString("lname"));
      				person.setEmail(rset.getString("email"));
      				person.setTel(rset.getString("tel"));
      				person.setCity(rset.getString("city"));
      				person.setId(rset.getInt("id"));
      			}
      		} catch (SQLException ex) {
      			System.out.println(ex.getMessage());
      		} finally {
      			try {
      				if (prestmt != null) {
      					prestmt.close();
      				}
      				if (conn != null) {
      					conn.close();
      				}
      			} catch (SQLException ex) {
      				System.out.println(ex.getMessage());
      			}
      		}
      		return person;
      	}
      
      Klasę Person.java uzupełnimy o metodę selectPerson().
        public void selectPerson(ActionEvent event)  {
      	  Database baza = new Database() ;
            UIParameter component = (UIParameter) event.getComponent().findComponent("editId");
            String id = component.getValue().toString();
            this.setPerson(baza.readPerson(id));
            return ;
         }
      
    4. Uzupełniamy brakujące wpisy w pliku messages.properties.
    5. Sprawdzamy poprawność działania aplikacji (rys.10 zawiera listę rekordów z bazy danych po wprowadzeniu dodatkowych operacji na rekordach).
      Lab02_view_appl_4
      Rys.10 Lista rekordów w bazie danych.
  7. Poprawa rekordów w bazie danych

    1. Formularz, który będzie umożliwiał poprawę danych umieścimy w skrypcie update.xhtml przedstawionym poniżej. Skrypt umieszczamy w katalogu "webapp".

      Skrypt update.xhtml - ( [listing dokumentu] [link do dokumentu]

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
            xmlns:h="http://xmlns.jcp.org/jsf/html"
            xmlns:f="http://xmlns.jcp.org/jsf/core"
            xmlns:c="http://xmlns.jcp.org/jsp/jstl/core">
      <head>
          <title>IGNORED</title>
      </head>
      
      <body>
      <ui:composition template="/templates/masterTemplate.xhtml">
          <ui:define name="windowTitle">
              #{msg.siteTitle}
          </ui:define>
      
          <ui:define name="content">
      
              <div id="content">
                  #{msg.titleFormUpdate}
                  <h:form>
                      <h:panelGrid columns="3" frame="hsides" cellspacing="3">
                          <h:outputLabel for="fname" value="#{msg.Firstname}" />
                          <h:inputText id="fname" value="#{personBean.fname}" />
                          <h:outputText value="" />
                          <h:outputLabel for="lname" value="#{msg.Lastname}" />
                          <h:inputText id="lname" value="#{personBean.lname}" />
                          <h:outputText value="" />
                          <h:outputLabel for="city" value="#{msg.City}" />
                          <h:inputText id="city" value="#{personBean.city}" />
                          <h:outputText value="" />
                          <h:outputLabel for="email" value="#{msg.Email}" />
                          <h:inputText id="email" value="#{personBean.email}" />
                          <h:outputText value="" />
                          <h:outputLabel for="tel" value="#{msg.Tel}" />
                          <h:inputText id="tel" value="#{personBean.tel}" />
                          <h:outputText value="" />
                      </h:panelGrid>
                      <h:inputHidden value="#{personBean.id}"></h:inputHidden>
                      <h:commandButton value="#{msg.saveLinkFormUpdate}" action="list"
                                       actionListener="#{personBean.updatePerson}" id="update" />
      
                  </h:form>
              </div>
          </ui:define>
      </ui:composition>
      </body>
      </html>
      
    2. Na koniec uzupełniamy zawartość klas Database.java i Person.java o funkcjonalność poprawy rekordu do bazie danych.
      Klasę Database.java uzupełniamy o metodę updatePerson().
      	public void updatePerson(Person person) {
      		try {
      			// System.out.println("Deleted record" + person.getId());
      			prestmt = conn.prepareStatement("UPDATE person SET fname=?,lname=?,email=?,city=?,tel=?  WHERE id = ?");
      			prestmt.setString(1, person.getFname());
      			prestmt.setString(2, person.getLname());
      			prestmt.setString(3, person.getEmail());
      			prestmt.setString(4, person.getCity());
      			prestmt.setString(5, person.getTel());
      			prestmt.setInt(6, person.getId());
      			prestmt.executeUpdate();
      		} catch (SQLException ex) {
      			System.out.println(ex.getMessage());
      		} finally {
      			try {
      				if (prestmt != null) {
      					prestmt.close();
      				}
      				if (conn != null) {
      					conn.close();
      				}
      			} catch (SQLException ex) {
      				System.out.println(ex.getMessage());
      			}
      		}
      		return;
      	}
      
      Klasę Person.java uzupełnimy o metodę updatePerson().
        public void updatePerson(ActionEvent event)  {
            Database baza = new Database() ;
            baza.updatePerson ( this.getPerson() ) ;
            return ;
         }  
      
    3. Uzupełniamy brakujące wpisy w pliku messages.properties.
    4. Sprawdzamy poprawność działania aplikacji (rys.11).
      Lab02_view_appl_5
      Rys.11 Poprawa rekordów w bazie danych.
  8. Usunięcie rekordu z bazy danych

    1. Realizacja tego punktu wymaga uzupełnienia zawartości klas Database.java i Person.java o funkcjonalność usunięcia rekordu z bazy danych.
      Klasę Database.java uzupełniamy o metodę deletePerson().
      	public void deletePerson(String id) {
      		try {
      			// System.out.println("Deleted record" + id);
      			Integer ident = Integer.parseInt(id);
      			prestmt = conn.prepareStatement("DELETE FROM person WHERE id = ?");
      			prestmt.setInt(1, ident);
      			prestmt.executeUpdate();
      		} catch (SQLException ex) {
      			System.out.println(ex.getMessage());
      		} finally {
      			try {
      				if (prestmt != null) {
      					prestmt.close();
      				}
      				if (conn != null) {
      					conn.close();
      				}
      			} catch (SQLException ex) {
      				System.out.println(ex.getMessage());
      			}
      		}
      		return;
      	}
      
      Klasę Person.java uzupełnimy o metodę deletePerson().
        public void deletePerson(ActionEvent event)  {
            Database baza = new Database() ;
            UIParameter component = (UIParameter) event.getComponent().findComponent("deleteId");
            String id = component.getValue().toString();
            baza.deletePerson(id);
            return ;
         }   
      
    2. Sprawdzamy poprawność działania aplikacji.
  9. Ustawienie aplikacji dla różnych języków (można zrealizować po uruchomieniu projektu)

    1. Opracowana aplikacja umożliwia w łatwy sposób wdrożenie ustawień regionalnych - wyświetlanie w strony w określonym języku. Zadanie można zrealizować na kilka sposobów. W ramach tego punktu przedstawione zostaną dwa sposoby.
      • wyświetlenie zgodnie z ustawieniami regionalnymi w przeglądarce
      • wybór ustawień przez użytkownika
    2. W celu wdrożenie odczytu ustawień regionalnych z przeglądarki należy zmodyfikować plik faces-config.xml oraz dodać określone pliki *.properties.
      • W pliku faces-config.xml należy dodać poniższy kod w ramach znacznika <application>
        <locale-config>
           <default-locale>pl</default-locale>
           <supported-locale>en</supported-locale>
        </locale-config>
        
        Następnie należy dodać dwa pliki w katalogu src/data: messages_pl.properties i messages_en.properties. Plik z rozszerzeniem "_pl" jest kopią pliku messages.properties, natomiast plik z roszerzeniem "_en" zawiera opis kluczy w języku angielskim. W ramach ustawień regionalnych można oprócz języka dodać kraj i roszerzenie będzie wtedy następujące "_en_US" (kraj dużymi literami). W tym przypadku należy to uwzględnić w pliku faces-config.xml.
      • Poprawność działania ustawień możemy sprawdzić zmieniając ustwienia regionalne w preferencjach przeglądarki.
    3. Realizacja programowego przestawiania ustawień regionalnych wymaga przygotowania klasy, która będzie modyfikowała w kontekscie ustawienia "locale". Realizacja tego zadania wymaga dodania do interfejsu użytkownika opcji wyboru języka oraz opracowania odpowiedniej klasy.
      • Na początek klasa modyfikują parametr "locale" w kontekście aplikacji. Klase umieszczamy w pakiecie "zti".

        Klasa LocaleChanger.java - ( [listing dokumentu] [link do dokumentu]

        package zti.zti_lab02;
        
        import java.io.Serializable;
        import java.util.Locale;
        
        import jakarta.annotation.PostConstruct;
        import jakarta.faces.context.FacesContext;
        
        import jakarta.enterprise.context.SessionScoped;
        import jakarta.inject.Named;
        
        @Named ("localeChanger")
        @SessionScoped
        
        public class LocaleChanger implements Serializable {
        
            private Locale locale;
        
            @PostConstruct
            public void init() {
                locale = FacesContext.getCurrentInstance().getExternalContext().getRequestLocale();
            }
        
            public Locale getLocale() {
                return locale;
            }
            /*
                public String polishAction() {
                    FacesContext context = FacesContext.getCurrentInstance();
                    context.getViewRoot().setLocale(new Locale("pl"));
                    locale = new Locale("pl");
                    return null;
                }
        
                public String englishAction() {
                    FacesContext context = FacesContext.getCurrentInstance();
                    context.getViewRoot().setLocale(Locale.ENGLISH);
                    locale = Locale.ENGLISH;
                    return null;
                }
            */
            public String getLanguage() {
                return locale.getLanguage();
            }
            public void setLanguage(String language) {
                locale = new Locale(language);
                FacesContext.getCurrentInstance().getViewRoot().setLocale(locale);
            }
        
        }
      • Kolejny krok, to dodanie możliwości wyboru języka w interfejsie użytkownika. Modyfikację zrealizujemy w pliku header.xhtml, który wyświetla się na każdej stronie.
        • Wybór wersji językowej z wykorzystaniem grafiki:

          Modyfikacja skryptu header.xhtml - ( [listing dokumentu] [link do dokumentu]

          			<h:form>
          				<h:commandLink action="#{localeChanger.polishAction}">
          					<h:graphicImage library="images" name="pl_flag.gif"
          						style="border:0px; margin-right:1em;" />
          				</h:commandLink>
          				<h:commandLink action="#{localeChanger.englishAction}">
          					<h:graphicImage library="images" name="en_flag.gif"
          						style="border:0px; margin-right:1em;" />
          				</h:commandLink>
          				
          				<h1>#{msgs.applTitle}</h1>
          
          				<!-- h:link value="Home"  id="index"  outcome="index" /-->
          				<!-- h:link value="Lista" id="list"  outcome="list"/-->
          				<!-- h:link value="Dodaj" id="add"   outcome="add"/-->
          				<h:commandButton value="#{msgs.linkHome}" id="home"
          					action="index?faces-redirect=true" />
          				<h:commandButton value="#{msgs.linkList}" id="list"
          					action="list?faces-redirect=true" />
          				<h:commandButton value="#{msgs.linkAdd}" id="add"
          					action="add?faces-redirect=true" />
          			</h:form>
          Następnie dodajemy do projektu ikony przy pomocy których będziemy wybierać język. Umieszczamy je w katalogu webapp/resources/images. Pliki zawierają ikony flag: pl_flag.gif i en_flag.gif.
        • Wybór wersji językowj z wykorzystaniem formularza:

          Modyfikacja skryptu header.xhtml - ( [listing dokumentu] [link do dokumentu]

                  <h:form>
                      <h:selectOneMenu value="#{localeChanger.language}" onchange="submit()">
                          <f:selectItem itemValue="en" itemLabel="English" />
                          <f:selectItem itemValue="pl" itemLabel="Polish" />
                      </h:selectOneMenu>
                  </h:form>
          
                  <h1>
                      #{msg.applTitle}
                  </h1>
                  <h:form>
                      <!-- h:link value="Home"  id="index"  outcome="index" /-->
                      <!-- h:link value="Lista" id="list"  outcome="list"/-->
                      <!-- h:link value="Dodaj" id="add"   outcome="add"/-->
                      <h:commandButton value="#{msg.linkHome}" id="home" action="index?faces-redirect=true" />
                      <h:commandButton value="#{msg.linkList}" id="list" action="list?faces-redirect=true" />
                      <h:commandButton value="#{msg.linkAdd}" id="add" action="add?faces-redirect=true" />
                  </h:form>
      • Do poprawnego działania przełącznika przy przejściu pomiedzy stronami wymagane jest dodanie znacznika <view > w projekcie w celu ustawiania wybranej przez użytkownika lokalizacji, która jest zapisana w klasie LocaleChanger. Znacznik wstawimy w skrypcie masterTemplate.xhtml, tak aby objął całość generowanej strony.
        <f:view locale="#{localeChanger.locale}">	
        <h:head>
        
        -- strona --
        
        </h:body>
        </f:view>
        
      • Na koniec sprawdzamy poprawność działania aplikacji (rys.12)
        Lab02_locale
        Rys.12 Wybór ustawień regionalnych w przeglądarce.
  10. Filtrowanie żądań w ramach aplikacji

    1. Na początek opracujemy filtr, który będzie rejestrował wszystkie żądania obsługiwane przez naszą aplikację. Do realizacji tego zadania wykorzystujemy odpowiednią klasę w języku Java typy filter. Poniżej odpowiedni kod realizujący opisaną funkcjonalność. Klasę umieścimy w pakiecie filters. Dane logowania na serwerze Tomcat uruchomionym w ramach IDE Eclipse pojawiają sie w logu konsoli.

      Klasa LogFilter.java - ( [listing dokumentu] [link do dokumentu]

      package zti.filters;
      
      import java.io.IOException;
      import java.util.Date;
      
      import jakarta.servlet.Filter;
      import jakarta.servlet.FilterChain;
      import jakarta.servlet.FilterConfig;
      import jakarta.servlet.ServletContext;
      import jakarta.servlet.ServletException;
      import jakarta.servlet.ServletRequest;
      import jakarta.servlet.ServletResponse;
      //import jakarta.servlet.annotation.WebFilter;
      import jakarta.servlet.http.HttpServletRequest;
      // import jakarta.servlet.http.HttpServletResponse;
      
      /**
       * Servlet Filter implementation class LogFilter
       */
      //@WebFilter("/LogFilter")
      public class LogFilter implements Filter {
      
          protected FilterConfig config;
          private ServletContext context;
          private String filterName;
      
          /**
           * Default constructor.
           */
          public LogFilter() {
              // TODO Auto-generated constructor stub
          }
      
          /**
           * @see Filter#destroy()
           */
          public void destroy() {
              // TODO Auto-generated method stub
          }
      
          /**
           * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
           */
          public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
              // TODO Auto-generated method stub
              // place your code here
              HttpServletRequest req = (HttpServletRequest) request ;
              context.log( " Zdalny serwer: " + req.getRemoteHost() + " - wywolal "
                      + req.getRequestURL() + " w dniu " + new Date() + "." +
                      "(Raport wygenerowany przez " + filterName + ".)" );
              // pass the request along the filter chain
              chain.doFilter(request, response);
          }
      
          /**
           * @see Filter#init(FilterConfig)
           */
          public void init(FilterConfig fConfig) throws ServletException {
              // TODO Auto-generated method stub
              this.config = fConfig;
              context = config.getServletContext();
              filterName = config.getFilterName();
          }
      
      }
      
      
    2. Przed realizacją drugiego filtru wykonajmy następujące zadanie.
      • Na początek sprawdzamy zawartość strony http://localhost:8080/zti_lab02 ... /list.jsf (rys. 13) i kod źródłowy tej strony (rys. 14 ),
        Lab02_filtr_01
        Rys.13 Strona: http://localhost:8080/zti_lab02 ... /list.jsf.
        Lab02_filtr_02
        Rys.14 źródło strony: http://localhost:8080/zti_lab02 ... /list.jsf (źródło strony).
      • Następnie sprawdzamy zawartość strony http://localhost:8080/zti_lab02 ... /list.xhtml (rys. 15) i również kod źródłowy strony (rys. 16).
        Lab02_filtr_03
        Rys.15 Strona http://localhost:8080/zti_lab02 ... /list.xhtml.
        Lab02_filtr_04
        Rys.16 Strona http://localhost:8080/zti_lab02 ... /list.xhtml (źródło strony).
      Aby uniknąć sytuacji umożliwiającej wyświetlenie kodu źródłowego aplikacji zawartej w skryptach *.xhtml bez sparsowania utworzymy klasę filtru zamieszczoną poniżej. Klasę umieszczamy również w katalogu "filters".

      Klasa XhtmlFilter.java - ( [listing dokumentu] [link do dokumentu]

      package zti.filters;
      
      import java.io.IOException;
      import java.io.PrintWriter;
      
      import jakarta.servlet.Filter;
      import jakarta.servlet.FilterChain;
      import jakarta.servlet.FilterConfig;
      import jakarta.servlet.ServletException;
      import jakarta.servlet.ServletRequest;
      import jakarta.servlet.ServletResponse;
      //import jakarta.servlet.annotation.WebFilter;
      import jakarta.servlet.http.HttpServletRequest;
      //import jakarta.servlet.http.HttpServletResponse;
      
      /**
       * Servlet Filter implementation class XhtmlFilter
       */
      //@WebFilter(filterName = "XhtmlFilter", urlPatterns = {"*.xhtml"})
      public class XhtmlFilter implements Filter {
      
          /**
           * Default constructor.
           */
          public XhtmlFilter() {
              // TODO Auto-generated constructor stub
          }
      
          /**
           * @see Filter#destroy()
           */
          public void destroy() {
              // TODO Auto-generated method stub
          }
      
          /**
           * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
           */
          public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
              // TODO Auto-generated method stub
              // place your code here
              //((HttpServletResponse)response).sendError(404);
              //((HttpServletResponse)response).sendRedirect("errors-page/Error404.html");
              // pass the request along the filter chain
              // chain.doFilter(request, response);
      
              HttpServletRequest req = (HttpServletRequest) request ;
              // ((HttpServletResponse)response).setStatus(404) ;
              response.setContentType("text/html");
              PrintWriter out = response.getWriter();
              out.println ("<html>");
              out.println ("<head><title>Error 404</title></head>");
              out.println ("<body>");
              out.println ("<h1>Przepraszamy !!!</h1>");
              out.println ("<h2> Wywolana strona: <span style='color:red'> " + req.getRequestURL() + "</span> jest niedostepna w serwisie ! ") ;
              out.println ("</body></html>");
          }
      
          /**
           * @see Filter#init(FilterConfig)
           */
          public void init(FilterConfig fConfig) throws ServletException {
              // TODO Auto-generated method stub
          }
      
      }
      
    3. Na koniec modyfikujemy plik web.xml wprowadzając informację o filtrach i ich kolejności uruchomienia w ramach aplikacji.
        <filter>
          <filter-name>LogFilter</filter-name>
          <filter-class>zti.filters.LogFilter</filter-class>
        </filter>
        <filter>
          <filter-name>XhtmlFilter</filter-name>
          <filter-class>zti.filters.XhtmlFilter</filter-class>
        </filter>
        <filter-mapping>
          <filter-name>LogFilter</filter-name>
          <url-pattern>/*</url-pattern>
          <dispatcher>REQUEST</dispatcher>
        </filter-mapping>
        <filter-mapping>
          <filter-name>XhtmlFilter</filter-name>
          <url-pattern>*.xhtml</url-pattern>
          <dispatcher>REQUEST</dispatcher>
        </filter-mapping>
    4. Na koniec sprawdzamy poprawność działania filtrów (rys.17).
      Lab02_filtr_05
      Rys.17 Strona http://localhost:8080/zti_lab02 ... /list.xhtml.
    5. Poniżej przedstawiono drzewo katalogów i plików w zrealizowanym projekcie (rys.18).
      Lab02_tree_project
      Rys.18 Drzewo plików w zrealizowanym projekcie.

Zaliczenie laboratorium

  1. Przesłanie zrealizowanego powyższego projektu w pliku *.war (z kodem źródłowym skryptów) z dodatkową funkcjonalnością
    Serwis JSF obsługujący bazę danych
    • Walidacja poprawności adresu e-mail - wykorzystać funkcjonalność kontrolki f:validateRegex
    • Dodać do bazy danych pole typu enumerowanego, który wybieramy z listy - wykorzystać funkcjonalność jednego z znaczników selekcji h:select...
    • Dodać do projektu arkusze styli - umieszczamy w katalogu webapp/resources/css/
    • Projekt opracować w wersji polskiej i angielskiej