09 października 2007

Konwertery w JavaServer Faces część 5

Przez ostatnie 4 odcinki w serii Konwertery w JavaServer Faces, stworzyłem aplikację, która korzysta z konwersji danej typu znakowego reprezentującego pracownika do typu Pracownik wymaganego w ziarnie zarządzanym JSF. Za pomocą atrybutu converter kontrolki h:selectOneMenu związałem klasę konwertera z konkretną kontrolką i podczas przetwarzania zlecenia JSF, wykonanie go gwarantowało odpowiedni typ w atrybucie ziarna pracownikAgent.

Poza użyciem klasy pl.jaceklaskowski.konwerter.TymczasowaBazaDanych, którą zajmę się w kolejnej części, jeszcze jedno miejsce mogło niepokoić - właśnie użycie atrybutu converter, który związywał widok z klasą konwertera. Jeśliby się zastanowić nad tym bardziej, to użycie atrybutu jest istotne dla osoby dostarczającej metody biznesowe w ziarnie zarządzanym JSF - pracownikAgent, podczas gdy dla twórcy strony strona4.jsp jest to kompletnie obojętne jaki i czy w ogóle konwerter będzie wykorzystywany. Najlepiej, gdyby konwersja odbywała się niezauważalnie dla obu stron - dostawcy metody biznesowej i autora strony (potencjalne miejsce na zrównoleglenie prac projektowych, gdzie kolejna osoba w zespole mogłaby pracować nad konwerterem bez wpływu na pozostałych członków). W/g mojego rozumienia wzorca Model-View-Controller (MVC), który służy do oddzielenia warstw w aplikacji, rolę konwersji bierze na siebie część kontrolera, który zarządza modelem i koordynuje widokami. W JSF rolę kontrolera przejmuje plik faces-config.xml (dokładniej jest to javax.faces.application.NavigationHandler i jego świta, którzy powodują, że JSF może przyjąć różne postacie, np. JBoss Seam, czy facelets, ale to jest temat na inny serial). Nie wchodząc w szczegóły, jeśli poszukujemy miejsca konfiguracji kontrolera to właśnie faces-config.xml jest tym miejscem.

<?xml version='1.0' encoding='UTF-8'?>

<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">
<converter>
<converter-for-class>pl.jaceklaskowski.konwerter.Pracownik</converter-for-class>
<converter-class>pl.jaceklaskowski.konwerter.PracownikConverter</converter-class>
</converter>
<managed-bean>
<managed-bean-name>pracownik</managed-bean-name>
<managed-bean-class>pl.jaceklaskowski.konwerter.Pracownik</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>pracownikAgent</managed-bean-name>
<managed-bean-class>pl.jaceklaskowski.konwerter.PracownikAgent</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
</faces-config>
Na pierwszy rzut oka różnica między obecną postacią faces-config.xml, a poprzednią może nie być widoczna. Jest to użycie elementu converter-for-class zamiast poprzedniego converter-id. Element converter-for-class związuje klasę konwertera z danym typem dla każdego jego wystąpienia w aplikacji, co niweluje potrzebę przypisywania go na stronie JSF.

<%@page contentType="text/html" pageEncoding="UTF-8"%>

<%@taglib prefix="f" uri="http://java.sun.com/jsf/core"%>
<%@taglib prefix="h" uri="http://java.sun.com/jsf/html"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Mini-aplikacja JSF - konwerter</title>
</head>
<body>
<f:view>
<h:form>
<h:selectOneMenu value="#{pracownikAgent.pracownik}">
<f:selectItems value="#{pracownikAgent.pracownicySelectItems}" />
</h:selectOneMenu>
<br/>
<h:commandButton value="Zatwierdź" actionListener="#{pracownikAgent.wykonajAkcje}"/>
<br/>
<h:messages showSummary="true" errorStyle="color: red" />
</h:form>
</f:view>
</body>
</html>
Różnicą w powyższej stronie strona5.jsp względem poprzedniej strona4.jsp jest usunięcie atrybutu converter. W ten sposób nasza strona nie wie, czy i jaki konwerter zostanie uruchomiony podczas przypisywania danych ze strony do ziarna zarządzanego JSF pracownikAgent. Zmiana niewielka, a zmniejsza powiązania między poszczególnymi elementami aplikacji opartej o JSF. Pozostałe elementy aplikacji pozostają niezmienione, co czyni tę zmianę nieinwazyjną acz bardzo potężną w możliwościach i skutkach, gdyż przypisanie konwertera do wszystkich miejsc użycia danego typu zawsze go aktywuje, podczas gdy poprzednie użycie konwertera było miejscowe, wybiórcze.

Uruchomienie aplikacji nie zmienia się w stosunku do poprzedniej części. Nieprzekonanych namawiam do uruchomienia samodzielnego w ramach zadania domowego i podzielenia się wnioskami.

Gotowy projekt aplikacji Konwerter dostępny jest jako jsf-konwerter-czesc5.zip.