We Love IT > Hinttech
Tags:

Echte UI-componenten in Facelets door scheiding van verantwoordelijkheden

Introductie

Een goed voorbeeld van een best practice in softwareontwikkeling is het principe van high cohesion van componenten en het principe van loose coupling van componenten. Het eerste houdt in dat iedere component een eigen, heldere verantwoordelijkheid heeft, terwijl het tweede impliceert dat alle componenten zoveel mogelijk onafhankelijk van elkaar zijn en alleen via stabiele interfaces interacten. Hierdoor wordt de onderhoudbaarheid, herbruikbaarheid en leesbaarheid van de code verbeterd. Een component blijft volledig verantwoordelijk voor de taken waarvoor het bedoeld is. Het is niet de bedoeling dat de code moet worden aangepast teneinde het component te kunnen gebruiken.

De voornaamste kracht van JavaServer Faces (JSF) is de mogelijkheid om user interface-componenten te ontwikkelen. Facelets is een uitstekende uitbreiding op JSF en maakt het eenvoudiger om herbruikbare user interface-componenten te implementeren voor op de JSF-pagina’s. Voorkomende elementen binnen een webapplicatie kunnen als Facelets worden ingericht en vervolgens binnen de pagina als custom tag worden opgenomen. Eenvoudige Facelets zonder achterliggende Java logica kunnen aan een xhtml-pagina worden toegevoegd zonder dat deze hier voor aangepast hoeft te worden.

Het wordt anders op het moment dat een Facelet meer ingewikkelde logica bevat, zoals een tabel die op alle kolommen sorteerbaar is. In dat geval moet de sorteerlogica in een backing bean geïmplementeerd worden. We willen deze sorteerlogica echter niet in de backing bean van de pagina opnemen omdat in dat geval de functionaliteit niet herbruikbaar is over meerdere pagina’s.

In dit artikel willen we laten zien hoe een JSF-applicatie opgebouwd kan worden, zodat Facelets op pagina’s gebruikt kunnen worden, zonder dat de backing bean en de xhtml van een pagina aangepast hoeven te worden (los van de toevoeging van het facelet aan de xhtml van een pagina). Kortom: hoe kan een Facelet als écht component gebruikt worden en bereiken we high cohesion en loose coupling in een JSF-applicatie.

Probleemstelling

Facelets voorziet op xhtml-niveau in user interface-componenten. Neem als voorbeeld een non-breaking space; om deze binnen JSF te realiseren, moet het volgende geschreven worden:

<f:verbatim>&#160;</f:verbatim>

Om dit overal binnen een applicatie uit te schrijven is lastig en foutgevoelig. Eenvoudiger is het om hiervoor een Facelet te schrijven:

Om dit als Facelet te gebruiken binnen een JSF-pagina, wordt de syntax:

<example:space/>

Bovenstaande is een eenvoudig maar ook krachtig voorbeeld van de kwaliteit van Facelets.

Bij een Facelet is het mogelijk om attributen door te geven op het xhtml-niveau. Als er een object meegegeven moet worden aan een Facelet wordt dat als volgt:

<example:facelet exampleAtribute="#{exampleBean}" />

De Facelet kan vervolgens van het attribuut “exampleAttribute” gebruik maken binnen de xhtml. Indien een Facelet een backing bean heeft, is het object “exampleAttribute” niet beschikbaar binnen de Java-code. Dus hoewel de Facelet op xhtml-niveau inzetbaar is als component zal de achterliggende Java-code van de pagina aangepast moeten worden wanneer een pagina gebruik maakt van een Facelet.

Voorbeeld applicatie

Als voorbeeld voor een Facelet wordt er gebruikt gemaakt van een zeer minimalistisch telefoonboek . Het telefoonboek bestaat uit een lijst waar namen en telefoonnummers aan toegevoegd kunnen worden. De gedachte achter dit voorbeeld is dat vele entiteiten een telefoonboek kunnen hebben. Zo kan een webapplicatie verschillende pagina’s bevatten waarbij voor een persoon en voor een organisatie een telefoonboek wordt bijgehouden. Aangezien het voor de presentatie van een telefoonboek niet uitmaakt in welke context het getoond wordt, kan het telefoonboek hergebruikt worden en zou het logisch zijn om eenfunctionaliteit als Facelet uit te voeren.

De pagina (hierna de “main view” genoemd) waar de Facelet gebruikt zal gaan worden heeft twee mogelijke toestanden: bewerken of bekijken. Er kan tussen deze twee toestanden worden gewisseld door de betreffende knop ‘bewerken’of‘bewaren/annuleren’.

De main view heeft een backing bean, die op zijn beurt een Persoon-object bevat dat een lijst met telefoonboekrecords bijhoudt. De Facelet biedt de mogelijkheid om met een knop een nieuw telefoonboekrecord aan te maken die vervolgens twee nieuwe invoervelden toont.

Main view:

AddressBook:

Zowel de backing bean van de main view als van de Facelet worden op request scope gedefinieerd [1]. Er moet een methode komen waarmee een nieuw record aangemaakt wordt. Dit betekent dat op Java-niveau een nieuw telefooboekrecordobject moet worden aangemaakt. Deze wordt toegevoegd aan de lijst op het Persoon-object.

De vraag is nu waar we deze nieuwRecord()methode zouden moeten plaatsen. De nieuwRecord() methode zal er ongeveer als volgt uit moeten komen te zien:

Er zijn drie plekken waar de nieuwRecord() methode geplaatst kan worden:
1. op het Persoon-object;
2. op de backing bean van de main view;
3. op de backing bean van de Facelet.

Main view bean met de nieuwRecord methode:

Facelet bean:

1. Op het Persoon-object

Als de nieuwRecord() methode op het persoonobject geplaatst wordt, is het persoonobject als attribuut meegegeven (zoals omschreven in het vorige paragraaf) en zou de Facelet deze methode aan kunnen roepen om een nieuw TelefoonBoekRecord-object toe te voegen.

Hoewel dit op het eerste gezicht een goede optie lijkt, houdt dit in dat het Persoon domein-object ‘vervuild’ wordt met viewgerelateerde methoden. Deze methoden worden als best practice op de view beans gehouden.

De andere opties zijn om de nieuwRecord() methode op de backing bean van de main view of op de backing bean van de Facelet te plaatsen. Beide opties brengen problemen met zich mee.

2. Op de backing bean van de main

view Als de nieuwRecord() methode op de backing bean van de main view geplaatst wordt, houdt dit in dat de main view weet heeft van het feit dat de telefoonboek-Facelet gebruikt wordt op de main view. Ook deze optie voldoet niet, aangezien het uitgangsprincipe was dat de Facelets kunnen worden toegevoegd zonder dat daarvoor de code van de pagina moet worden aangepast.

3. Op de backing bean van de Facelet

De backing bean van de Facelet is wat betreft design het beste, maar is met de huidige mogelijkheden van JSF niet mogelijk. Zoals omschreven is het niet mogelijk om via de xhtml van een pagina attributen aan de backing bean van een Facelet door te geven. In de nieuwRecord() methode hebben we een adresBoekHolder object nodig om aan dat object het nieuwe TelefoonBoekRecord toe te voegen. Een mechanisme is nodig om via de xhtml van een pagina een Java-object door te geven aan de backing bean van een Facelet.

De SetProperty tag

De SetProperty tag is geschreven om het mogelijk te maken om via de xhtml van een pagina, objecten door te geven aan een backing bean van een Facelet. De SetProperty tag overerft van het JSF UIComponentBase-object en maakt gebruik van de standaard JSF- fasesom een object door te geven. JSF kent zes fases:restore view, apply request values, process validations, update model values, invoke application en render response. Door in de restore view fase een waarde op de target bean te zetten, heeft vanaf dat moment de target bean de doorgegeven waarde tot zijn beschikking. Het gebruik van de SetProperty tag is zeer eenvoudig:

Door middel van de SetProperty tag is het in het voorbeeld van de vorige paragraaf nu mogelijk geworden om de nieuwRecord() methode op de backing bean van de Facelet te plaatsen. Via de SetProperty tag kan het Persoon-object worden doorgegeven. De backing bean van de Facelet beschikt daardoor over het AddressBookHolder-object. De main view backing bean heeft nu geen enkele wetenschap welke Facelets gebruikt moeten worden. De main view backing bean bevat nu alleen logica en domeinobjecten binnen zijn eigen context, logica gedelegeerd naar Facelets valt volledig buiten de main view, zowel voor de xhtml als de backing bean.

Luisteren naar events

Tot nu toe is de eenvoudige situatie besproken waarbij de Facelet onafhankelijk van de main view kan opereren. In realworldapplicaties is het vaak noodzakelijk dat een Facelet reageert op handelingen die op de main view plaatsvinden. Daarom is in het voorbeeld de main view twee mogelijke toestanden gegeven: bewerken en bekijken. Wanneer de main view in de ‘bewerken’-toestand verkeert is het mogelijk om de huidige telefoonboekrecords te wijzigen of nieuwe toe te voegen. Voor het voorbeeld wordt gesteld dat de telefoonboekrecords gevalideerd moeten worden op het moment dat de gebruiker op de ‘bewaren’-knop drukt.

Hier geldt ook dat de validatielogica op het Persoon-object of op de backing bean van de main view geplaatst kan worden. Maar om dezelfde redenen als hierboven genoemd, is dit niet wenselijk. Kortom, de validatie zou op de backing bean van de Facelet geplaatst kunnen worden.Dat kan gerealiseerd worden door een combinatie van de SetProperty tag en het Observer pattern:

Door een SetProperty tag toe te voegen aan de Facelet waarmee de backing bean van de main view doorgegeven kan worden, kan de backing bean van de Facelet als Observer van de backing bean van de main view functioneren. De backing bean van de main view is in dit geval het Subject. Door dit pattern toe te passen is de Facelet dus in staat om te reageren op acties van de main view. Onderstaande code toont de code van ons voorbeeld, ditmaal met het Observer pattern toegepast.

Main view bean als Subject:

Facelet bean als Observer:

Samenvattend

Dit artikel beschrijft een design pattern voor Facelets-applicaties, waarmee de herbruikbaarheid, onderhoudbaarheid en leesbaarheid van code verbeterd wordt. De backing beans van pagina’s in de applicatie dienen als Subject in het Observer-pattern uitgevoerd te worden. Hierdoor kunnen Facelets reageren op acties die op de main view plaatsvinden.

Door gebruik te maken van de SetProperty tag binnen Facelets is het niet langer nodig dat de backing bean van een pagina kennis heeft van de Facelets die gebruikt worden op een pagina. Hierdoor is het in een later stadium makkelijk om een pagina uit te breiden met een Facelet zonder verder de code aan te hoeven passen en is het mogelijk om op een centrale plek de logica van de Facelet uit te breiden of aan te passen.

Referenties

[1] Tuning JSF Applications – Session size matters, http://www.nljug.org/pages/events/content/jspring_2008/sessions/00017/

Over de auteurs

Jonck van der Kogel - Softwareengineer en manager Java Competence Center, HintTech. Jonck van der Kogel heeft ruim 10 jaar ervaring met softwaredevelopment. Sinds 2007 is Jonck werkzaam als software-engineer bij HintTech waar hij zich heeft kunnen profileren als Java-specialist. Hij richt zich met name op J2EE, webapplicaties en webservices. Als manager van het Java Competence Center van HintTech draagt Jonck zorg dat HintTech qua technologische kennis een koploper blijft.

Eelco Klaver - Senior consultant, E.Consulting, HintTech. Eelco Klaver is sinds 2006 werkzaam als senior consultant bij E.Consulting, dat zich richt op Enterprise Java consultancy en het geven van trainingen. Klaver houdt zich bezig met Enterprise Java architectuur, security workshops, software reviews en security audits. Eelco heeft ruim 12 jaar hands-on ervaring met het ontwikkelen van enterprise applicaties in Java en J2EE bij verschillende werkgevers en voor diverse grote opdrachtgevers. De laatste jaren heeft hij zich gespecialiseerd in de beveiligingsaspecten van op J2EE gebaseerde enterprise applicaties.

Lees meer over HintTech
Advertentie