the definitive guide to jsf in java ee 8: building web applications with javaserver faces

720

Upload: others

Post on 11-Sep-2021

19 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces
Page 2: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

BaukeScholtzand

ArjanTijms

TheDefinitiveGuidetoJSFinJavaEE8BuildingWebApplicationswithJavaServerFaces

Page 3: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

BaukeScholtz

Willemstad,CuraçaoArjanTijms

Amsterdam,Noord-Holland,TheNetherlands

AnysourcecodeorothersupplementarymaterialreferencedbytheauthorinthisbookisavailabletoreadersonGitHubviathebook’sproductpage,locatedatwww.apress.com/9781484233863.Formoredetailedinformation,pleasevisithttp://www.apress.com/source-code.

ISBN978-1-48423386-3

e-ISBN978-1-4842-3387-0

https://doi.org/10.1007/978-1-4842-3387-0

LibraryofCongressControlNumber:2018942178

©BaukeScholtz,ArjanTijms2018

Thisworkissubjecttocopyright.AllrightsarereservedbythePublisher,whetherthewholeorpartofthematerialisconcerned,specificallytherightsoftranslation,reprinting,reuseofillustrations,recitation,broadcasting,reproductiononmicrofilmsorinanyotherphysicalway,andtransmissionorinformationstorageandretrieval,electronicadaptation,computersoftware,orbysimilarordissimilarmethodologynowknownorhereafterdeveloped.

Page 4: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Trademarkednames,logos,andimagesmayappearinthisbook.Ratherthanuseatrademarksymbolwitheveryoccurrenceofatrademarkedname,logo,orimageweusethenames,logos,andimagesonlyinaneditorialfashionandtothebenefitofthetrademarkowner,withnointentionofinfringementofthetrademark.Theuseinthispublicationoftradenames,trademarks,servicemarks,andsimilarterms,eveniftheyarenotidentifiedassuch,isnottobetakenasanexpressionofopinionastowhetherornottheyaresubjecttoproprietaryrights.

Whiletheadviceandinformationinthisbookarebelievedtobetrueandaccurateatthedateofpublication,neithertheauthorsnortheeditorsnorthepublishercanacceptanylegalresponsibilityforanyerrorsoromissionsthatmaybemade.Thepublishermakesnowarranty,expressorimplied,withrespecttothematerialcontainedherein.

DistributedtothebooktradeworldwidebySpringerScience+BusinessMediaNewYork,233SpringStreet,6thFloor,NewYork,NY10013.Phone1-800-SPRINGER,fax(201)348-4505,[email protected],orvisitwww.springeronline.com.ApressMedia,LLCisaCaliforniaLLCandthesolemember(owner)isSpringerScience+BusinessMediaFinanceInc(SSBMFinanceInc).SSBMFinanceIncisaDelawarecorporation.

Page 5: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Tocaffeineandour(notso)patientwives.

Page 6: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

TableofContents1. Chapter1:History

1. IntheBeginning...2. TheAdolescentYears3. OntoMaturity4. Rejuvenation

2. Chapter2:FromZerotoHelloWorld

1. InstallingJavaSEJDK

1. WhatAboutJavaEE?

2. InstallingPayara

1. HowAboutOtherServers?

3. InstallingEclipse

1. ConfiguringEclipse2. InstallingJBossToolsPlug-in3. IntegratingNewServerinEclipse

4. CreatingNewProjectinEclipse

1. CreatingtheBackingBeanClass2. CreatingtheFaceletsFile3. DeployingtheProject

5. InstallingH2

Page 7: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

1. ConfiguringDataSource2. ConfiguringJPA3. CreatingtheJPAEntity4. CreatingtheEJBService5. AdjustingtheHelloWorld

3. Chapter3:Components

1. StandardHTMLComponents2. StandardCoreTags3. LifeCycle

1. RestoreViewPhase(FirstPhase)2. ApplyRequestValuesPhase(SecondPhase)3. ProcessValidationsPhase(ThirdPhase)4. UpdateModelValuesPhase(FourthPhase)5. InvokeApplicationPhase(FifthPhase)6. RenderResponsePhase(SixthPhase)

4. AjaxLifeCycle5. ViewBuildTime6. ViewRenderTime7. ViewState8. ViewScope9. PhaseEvents10. ComponentSystemEvents11. CustomComponentSystemEvents12. JSTLCoreTags13. ManipulatingtheComponentTree

4. Chapter4:FormComponents

1. Input,Select,andCommandComponents2. Text-BasedInputComponents3. File-BasedInputComponent4. SelectionComponents5. SelectItemTags6. SelectItemGroup7. LabelandMessageComponents

Page 8: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

8. CommandComponents9. Navigation10. AjaxifyingComponents11. NavigationinAjax12. GETforms13. StatelessForms

5. Chapter5:ConversionandValidation

1. StandardConverters

1. <f:convertNumber>2. <f:convertDateTime>

2. StandardValidators

1. <f:validateLongRange>/<f:validateDoubleRange>2. <f:validateLength>/<f:validateRegex>3. <f:validateRequired>4. <f:validateBean>/<f:validateWholeBean>

3. ImmediateAttribute4. CustomConverters5. CustomValidators6. CustomConstraints7. CustomMessages

6. Chapter6:OutputComponents

1. Document-BasedOutputComponents2. Text-BasedOutputComponents3. Navigation-BasedOutputComponents4. Panel-BasedOutputComponents5. DataIterationComponent

1. Editable<h:dataTable>2. Add/RemoveRowsin<h:dataTable>3. SelectRowsin<h:dataTable>4. DynamicColumnsin<h:dataTable>

Page 9: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

6. ResourceComponents7. Pass-ThroughElements

7. Chapter7:FaceletsTemplating

1. XHTML2. TemplateCompositions3. SinglePageApplication4. TemplateDecorations5. TagFiles6. CompositeComponents

1. RecursiveCompositeComponent

7. ImplicitELObjects

8. Chapter8:BackingBeans

1. Model,View,orController?2. ManagedBeans3. Scopes

1. @ApplicationScoped2. @SessionScoped3. @ConversationScoped4. @FlowScoped5. @ViewScoped6. @RequestScoped7. @Dependent

4. Whichscopetochoose?5. WhereIs@FlashScoped?6. Managedbeaninitializationanddestruction7. InjectingJSFvendedtypes8. EagerInitialization9. Layers10. NamingConventions

9. Chapter9:ExceptionHandling

Page 10: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

1. CustomErrorPages2. AjaxExceptionHandling3. ViewExpiredExceptionHandling4. IOExceptionHandling5. EJBExceptionHandling

10. Chapter10:WebSocketPush

1. Configuration2. Usage3. ScopesandUsers4. ChannelDesignHints5. One-TimePush6. StatefulUIUpdates7. Site-WidePushNotifications8. KeepingTrackofActiveSockets9. DetectingSessionandViewExpiration10. BreakingDownMojarra’sf:websocketImplementation

11. Chapter11:CustomComponents

1. ComponentType,Family,andRendererType2. CreatingNewComponentandRenderer3. ExtendingExistingComponent4. ExtendingExistingRenderer5. CustomTagHandlers6. PackaginginaDistributableJAR7. ResourceDependencies

12. Chapter12:SearchExpressions

1. RelativeLocalIDs2. AbsoluteHierarchicalIDs3. StandardSearchKeywords4. CustomSearchKeywords

13. Chapter13:Security

Page 11: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

1. JavaEESecurityOverviewandHistory2. ProtectAccesstoResources

1. Excluded2. Unchecked3. ByRole

3. SettingtheAuthenticationMechanism4. SettingtheIdentityStore5. ProvidingOurCustomJSFCode6. Caller-InitiatedAuthentication7. RememberMe

1. ActivatingRemember-MeService

8. LoggingOut9. CustomPrincipals10. ConditionallyRenderingBasedonAccess11. Cross-SiteRequestForgeryProtection12. WebParameterTamperingProtection13. Cross-SiteScriptingProtection14. SourceExposureProtection

14. Chapter14:Localization

1. HelloWorld,Olámundo,

2. Configuration3. ReferencingBundleinJSFPage4. ChangingtheActiveLocale5. OrganizingBundleKeys6. LocalizingConversion/ValidationMessages7. ObtainingLocalizedMessageinaCustom

Converter/Validator8. LocalizingEnums9. ParameterizedResourceBundleValues10. Database-BasedResourceBundle11. HTMLinResourceBundle

Page 12: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

15. Chapter15:Extensions

1. ExtensionTypes2. ExtendingCDIArtifacts3. ExtendingClassicalArtifacts4. Plug-ins5. DynamicExtensions

1. ApplicationConfigurationPopulator2. TheApplicationMainClass

6. LocalExtensionandWrapping7. Introspection

16. Index

Page 13: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

AbouttheAuthorsandAbouttheTechnicalReviewer

AbouttheAuthors

BaukeScholtzisanOracleJavaChampion,amemberoftheJSF2.3ExpertGroup,andthemaincreatoroftheJSFhelperlibraryOmniFaces.OntheInternet,heismorecommonlyknownasBalusC,whoisamongthetopusersandcontributorsonStackOverflow.BaukehasintegratedseveralOmniFacessolutionsintoJSF2.3.HeisawebapplicationspecialistandconsultsorhasconsultedforMercury1Limited,MyTutor,NavaFinance,LinkPizza,ZEEF,M4N/Zanox,ITCA,RDC,andmoreclientsfromfintech,affiliatemarketing,socialmedia,andmoreaspartofhis17yearsofexperience.ThisbookoffersBauketheopportunitytogointodepthtoanswer

Page 14: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

mostfrequentlyaskedquestionsandcorrectlysolvemostcommonlyencounteredproblemswhileusingJSF.

ArjanTijmsworksforPayaraServicesLtdandisaJSF(JSR372)andSecurityAPI(JSR375)ExpertGroupmember.Heistheco-creatorofthepopularOmniFaceslibraryforJSF,whichwasa2015Duke’sChoiceAwardwinner,andisthemaincreatorofasetoftestsfortheJavaEEauthenticationSPI(JASPIC)usedbyvariousJavaEEvendors.ArjanholdsanMScdegreeinComputerSciencefromtheUniversityofLeiden,TheNetherlands.WritingaboutthistopicwasanaturalchoiceforArjan;Hehasalreadywrittenmuchaboutitonhisblogandwantedtoexpandthatbycontributingtoabook.

AbouttheTechnicalReviewer

Page 15: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Chád(“Shod”)Darbyisanauthor,instructor,andspeakerintheJavadevelopmentworld.AsarecognizedauthorityonJavaapplicationsandarchitectures,hehaspresentedtechnicalsessionsatsoftwaredevelopmentconferencesworldwide(intheUnitedStates,UK,India,Russia,andAustralia).Inhis15yearsasaprofessionalsoftwarearchitect,he’shadtheopportunitytoworkforBlueCross/BlueShield,Merck,Boeing,RedHat,andahandfulofstartupcompanies.

ChádisacontributingauthortoseveralJavabooks,includingProfessionalJavaE-Commerce(WroxPress),BeginningJavaNetworking(WroxPress),andXMLandWebServicesUnleashed(SamsPublishing).ChádhasJavacertificationsfromSunMicrosystemsandIBM.HeholdsaBSincomputersciencefromCarnegieMellonUniversity.YoucanvisitChád’sblogatwww.luv2code.comtoviewhisfreevideotutorialsonJava.YoucanalsofollowhimonTwitterat@darbyluvs2code.

Page 16: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1) (2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_1

1.History

BaukeScholtz andArjanTijms

Willemstad,Curaçao Amsterdam,Noord-Holland,TheNetherlands

ThischapterdescribesthehistoryofJSF,startingfromitsearlyconceptionandendingwherewearetodayatthemomentofwriting.We’lldiscusshowtheJSFAPI(applicationprogramminginterface)itselfevolved,whichimportanteventstookplaceduringthatevolution,andwhosomeofthepeoplewerethatwereinvolvedinallofthis.

Thisisinnowayacompletedescriptionofthehistoryandthereadershouldtakenoticeofthefactthatmanymoreeventstookplaceandmanymorepeoplewereinvolvedthanwewereabletomentionhere.

IntheBeginning...JSFgoesbackalongtime.ItsinitialJSR,JSR127,startedin2001.AtthattimetheStrutswebframeworkwaswildlypopular,althoughitwasn’tthatlongagothatitwasreleaseditself(around2000).DespiteStruts’popularity,alargenumberofotherwebframeworkswereinuseintheJavaspace,andnewoneswerepoppingupallthetime.JavaServer

1 2

Page 17: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Faces(JSF)wasconceivedasanattempttobringastandardizedMVC(model-view-controller)webframeworkbaseintotheoverallJavaEEplatform.

Controversiesarequitecommoninthewebframeworkspace,andJSFisnoexceptionhere.RightatthestartofitsinceptiontherewasabigcontroversywhereApacheopposedthecreationofJSFonthebasesthatApacheStrutsalreadyexistedandaclosedsourcealternativewouldhavelittlevalue.ApachethereforevotedagainstthecreationofJSFwiththefollowingcomment:

ThisJSRconflictswiththeApacheopensourceprojectStruts.ConsideringSun’scurrentpositionthatJSRsmaynotbeindependentlyimplementedunderanopensourcelicense,weseelittlevalueinrecreatingatechnologyinaclosedenvironmentthatisalreadyavailableinanopenenvironment.

TotheextentthatthisJSRextendsbeyondStrutstoday,wewouldencouragetheSundevelopersproposingthisJSRtojointheSundevelopersalreadyleadingStrutstocreateanopensolutionatApache,somethingwhichwhenfinishedwouldbeassuredofbeingabletobeimplementedasopensource.

EventuallytheconflictwasresolvedwhenafteraboutayearintotheprocessspecleadAmyFowler(fromSwingfame)wasreplacedbyCraigMcClanahan,theveryfatheroftheStrutsprojectthatJSFwassaidtobecompetingwith.Theopensourcerestrictionwasliftedaswell,andtheopensourceJSFimplementation,calledMyFaces,wasdevelopedinparallelwiththe(thennameless)RIandhencethespecificationitself.MyFacesinitiallystartedasanLGPLlicensedprojectatsourceforge.netinDecember2002andhad

Page 18: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

aninitial0.1releaseconformingtowhatwasthencalledan“EarlyAccessSpecification”inJanuary2003.

OpensourceimplementationsarethemostcommonimplementationsinJavaEE8,andthere’sbarelyanyEEspecificationatthetimeofthiswriting(2018)that’sstillimplementedasclosedsource.In2001,however,thiswasnotjustuncommon;itwasactuallynotallowedfornewJSRs.Allowingforanopensourceimplementationwasthereforequiteachange,andthehonorfelltoJSFtobethefirstofitskindforwhichthiswasallowed.

Despitetheopensourceimplementationbeingallowed,theactualdevelopmentofthespecwasstilldoneinsecretandbehindcloseddoors.Therewasnopublicmailinglist,andnotracker(e.g.,aJIRAinstance)forthepublictocreateissuesorexpresswishes.Occasionallyinterviewswerebeingdone,andinthefallof2002bythenformerspecleadAmyFowlerdidrevealquiteafewdetailsaboutJSF,butlargelytheprojectwasshroudedinmysteryforthegeneralpublic.

TheteambehindJSFwas,however,hardatwork.Thefirste-mailtotheinternalJSR-127listwassentonAugust17,2001.Aswithmostprojects,theteamspenttheinitialmonthsongatheringrequirementsandlookingattheexistingcompetingproducts.Apackagenamewaschosenaswell.Theinitialplaceholderpackage,whichwas"javax.servlet.ui",now"javax.faces",waschosenasthepackagetouse.Theveryfirsttechnicalarchitecturetobeconsideredwasthecomponentmodel.Foracomponent-basedMVCframeworkthisisobviouslyoneofthemostimportantaspects.Duringthelastmonthof2001andthefirsttwomonthsof2002theteamlookedatwhatisnowknownastheManagedBean(called“ObjectManager”then).

Page 19: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Managedbeanswiththeirscopes,names,anddependencyinjectionareclearlyanothercornerstoneoftheJSFframework.Eventsandthemodelbehinditwerebeinglookedataswellduringthattimeframe.

Inthesecondquarterof2002twoothercornerstonesofJSFwerediscussed:theExpressionLanguage(inspiredbyJSTL),whichisinstrumentalfortheso-calledbindingsofbeansfromatemplatetobackingcode,andthefactoryfinder,whichallowedkeypartsofJSFtobereplacedandalthoughperhapsnotfullyrealizedatthetimemayhavecontributedgreatlytoJSFstillbeingrelevantsome16yearslater.

ItwasinthissamequarterthatCraigMcClanahantookoverasspeclead,fatherofStrutsandarchitectofTomcat’sServletcontainer,tookover.NotlongafterthediscussionaboutusingJSPstarted,adiscussion,perhapsunbeknownsttotheteamatthetime,thatwould,unfortunately,havearathernegativeimpactonJSFlateron.Aroundtheendoftheyear2002,EdBurns,wholikeMcClanahanhadalsoworkedonTomcatbefore,joinedtheteamasco-speclead.BurnsisthepersonwhowouldeventuallybecomethemainspecleadofJSFforwelloveradecade.

Whiletheteamcontinuedtoworkonthingsliketheaforementionedmanagedbeansandtheso-calledvaluebinding,whichistheJavarepresentationofthealsoaforementionedexpressionlanguagebinding,thefirstdarkcloudappearedwheninthespringof2003teammemberHansBergstenrealizedthattherewereveryrealandmajorissueswithusingJSPasthetemplatinglanguageforJSF.Hebroughttheseconcernstotheteam,butultimatelytheyweren’taddressedandinsteadthefollowingmonthswerespent,amongotherthings,onavariantofthevaluebinding;itlater

Page 20: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

onbecameclearthatthemethodbindingandthestatesavingmechanismwereanotherofJSF’slessthanidealimplementations.

JSF1.0anditsstillnamelessRIwereeventuallyreleasedonMarch11,2004—coincidentally,ameretwoweeksbeforethereleaseofanotherframeworkthat’sstillstrongtoday,Spring1.0.MyFacesreleasedits1.0.0alphaversiononlydayslater,onMarch19.It’sperhapsaninterestingobservationthatJSFwentfinalwithafull-fledgedXML-baseddependencyinjection(DI)frameworkjustbeforeSpring,whichislargelyknownforitsDI,wentfinal.

JSF1.0wasgenerallywellreceived;despitearathercrowdedmarketwithcompetitorssuchasTapestry,WebObjects,Velocity,andCocoonoperating,notlessthanthreebooksfromwriterssuchasHorstCaymannandHansBergstenappearedinthemonthsafter,andtheeXoplatform(aDigitalCollaborationPlatform)startedusingJSFrightaway.

HansBergsten’searlierconcerns,however,becomepainfullyclearalmostjustasquickly;theJSPtechnologyisbasedonprocessingatemplatefromstarttoend,immediatelywritingtotheresponseastagsareencountered.JSF,however,requiresaphasedapproachwherecomponentsneedtobeabletoinspectandactonthecomponenttree,whichisbuiltfromthetagsonthepage,beforestartingtowriteanythingtotheresponse.Thismismatchledtomanystrangeissues,suchascontentdisappearingorbeingrenderedoutoforder.

OnlythreemonthsaftertheintroductionofJSF,HansBergstenmadeastrongcaseofdroppingJSPinhislegendaryarticle“ImprovingJSFbyDumpingJSP.”ThereBergstenexplainshowill-suitedJSPisforuseatemplatelanguageinJSF,buthealsopresentsaglimmerofhope;becauseofJSF’s

Page 21: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

greatsupportforextendibility,it’srelativelyeasytointroducealternativetemplatingsimplybyreplacingtheso-calledviewhandler,somethingwhichJSFexplicitlyallows.Itwould,however,takefivelongyearsuntilJSFwouldindeedshipwithamoresuitableviewtemplatinglanguage,andeventhoughJSPhadbeenessentiallydeprecatedatthatpointit’sstillpresentinJSFatthetimeofwriting.

TheAdolescentYearsBackin2004anotherfirstbefellJSF;onJune28EdBurnsannouncedthatthesourceoftheRIwasreleasedbySun.ThisrepresentedamajormilestoneasbeforethatdatemosttechnologyinactiveusebySunwasclosedsource.InitiallythesourcewaslicensedunderthesomewhatexoticJRL,butlaterthiswouldbechangedtoduallicenses,GPLwithclasspathexceptionandCDDL.Atthesametimeasthisannouncement,thetraditionwasestablishedthateverynewfeatureorbugfixshouldbeaccompaniedbyatest,andthatallexistingtestsshouldbeexecutedbeforecommittingthechange.Some14yearslaterthere’salargelydifferentsetofpeopleworkingontheRIsource,andtheprojectstructureandcodeconventionshavechangedaswell,butthetest-driventraditionisstillbeingupholdinitsoriginalform.

AtthatpointEdBurnsdecidedtofocusmoreonthespecificationaspectsofJSFastheJSF1.2specworkhadstartedrightaway,andJayashriVisvanathan,oneoftheearlyteammembers,tookontheleadroleconcerningtheimplementationaspects,withRyanLubke,workingastheTCK(testing)engineer.

Stillonlyafewmonthsold,avarietyofcomponent

Page 22: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

librariesforJSFhadalreadystartedtopopup,althoughallofthemcommercial.AmongthosewastheonefromOracle,ADFFaces.ADFFaceswasputonOracle’sroadmapwellbeforeJSF1.0wentfinal,andthefirstearlyaccessreleasewaspresentedonAugust17,2004.ItsleadwasAdamWiner,whorepresentedOracleintheteamthatcreatedJSF1.0.ADFFacesprimarilycontainedasetofrichcomponents,butalsoadialogframework,andremarkablyalreadyfeaturedpartialpagerendering(PPR),quiteabitaheadofthelatercropofAJAXsolutions.ADFFacesalsocontaineda“foreach”tag(af:forEach)thatactuallyworked.AdamWinerexplainedintheseearlydaysthatsuchtagisnotquitetrivialtobuildbutpromisedthatOraclewouldcontributetheknowledgebacktoJSFitself.

TheADFFacescomponentsoriginatedmostlyfromtheearlierUserInterfaceXML(UIX)framework,ofwhichAdamWinerwastheleadarchitectaswell.EarlierversionsofUIXusedthenames“Cabo,”“Baja,”and“Marlin.”UIXwasarichclientframeworkforuseinthebrowser.WithJSFsharingmorethanafewsimilaritiestoUIX,andwithitslead,AdamWiner,beingpartoftheoriginalJSFteam,it’sperhapsnotunreasonabletosurmisethatUIXinfluencedJSF.Suchsimilaritiesincludetheconceptofcomponentswithseparaterenderers,JSPtaghandlersanddeclarativeoptionstocomposeapage,andtheabilitytoinstantiatethosesamecomponentsprogrammaticallyinJava.Therewasevenaconceptuallysimilardatabinding,althoughwithalesselegantsyntax.Insteadof,say,value="#{user.age}",UIXwouldusedata:value="age@user"butalsorequiredakindofproducertobedefinedoneachpagetodeclarewhere“user”

Page 23: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

comesfrom,andthennestthepage’scontentwithinthatdeclaration.Bycontrast,JSFandELhavealwaysusedglobaldefinitionsandleftituptotheusertoavoidnameclashes.

Oneofthefirst,ifnotthefirstopensourcecomponentlibraryin2004wasMatthiasUnverzagt’sOurFaces.AsJSFdidnothaditsownresourceAPI(applicationprogramminginterface)atthetimetoserveupthingslikeimages,OurFacesrequiredaServlettobeaddedtoweb.xml,theso-calledSkinServlet

(ourfaces.common.webapp.SkinServlet).ThesignificanceofthisisthatitbecamearathercommonthingforJSFlibrariesinthosedaystoasktheirusers:addsomethingmanuallytoweb.xmlbeforethecomponentlibrarycanbeused.

Mostofthelastmonthsof2004andearlymonthsof2005werespentbytheJSF1.2expertgroup(EG)workingonvariousJSPandELissues,suchastheJSTL<c:forEach>supportandthegenerationofIDsinJSP,aswellasonthedreaded“contentinterweaving”issue,whichreferstotheaforementionedcontentthatappearsatwrongplacesintheresponsewhenrendering.

WhileOurFacesmayhavebeenoneofthefirstcomponentlibraries,itdidn’tlastandfewwillrememberitorhaveevenheardaboutittoday.Thisisnotquitethesameforanotherframeworkthathasitsrootsinearly2005,namely,AlexanderSmirnov’sTelamonframework,laterrenamedAjax4jsf.ThisframeworkwasoneofthefirstofitskindthatcombinedJSFandthethennewandfreshAJAXtechnology.ThebeautyofAjax4JsfwasthatitcouldaddAJAXsupporttoexistingcomponents,whichweren’tbuiltwithAJAXsupportinmind

Page 24: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

atallbyenclosingthemamongothersinthe<a4j:region>tag.ThistechnologywasincorporatedintheExadelVisualComponentPlatform,whichwasreleasedinMarch2006andwouldlaterberenamedRichFaces,andwouldbecomeoneofthemostmemorableJSFcomponentlibraries.

AtaroundthesametimeAlexanderSmirnovstartedworkonwhateventuallywouldbecomeRichFaces,acompanycalledICEsoftstartedworkingonaJSFcomponentlibrary.ICEsofthadbeeninbusinessforacoupleofyearsandhadbeenworkingonaproductcalledICEbrowser,aJava-basedbrowser,andaproductcalledICEbrowserbeans,whichwere“lightweight,configurableJavabeancomponentsthatcanberapidlyintegratedintoJavaclientapplications.”DuringJavaOne2005ofthatyear,on27June,ICEsoftannouncedtheiritscomponentlibraryforJSF—ICEfaces.ThiswasbasedonAJAXaswellbutincorporatedAJAXdirectlyintothecomponents.ICEsoftcalleditsspecifictechnique“patentpendingDirect-to-DOM™,”whichbasicallymeantthatchangescomingfromtheserverweredirectlyinjectedintotheDOMtreestructureofawebpage.Afinalversionwasn’tavailablerightawaythough,butanearlyaccessreleasewasprovided.Thiswasclosedsourcebutcost-free.

Meanwhile,JSFEGmemberJacobHookom,inspiredbyHansBergsten’sconcernsabouttheunsuitabilityofJSP,grabbedthebullbythehornsandstartedworkinghimselfonthatalternativetemplatinglanguageenvisionedbyBergsten.InAugust2005thisworkhadprogressedintoausableinitialversion.Thenameofthistemplatinglanguage?Facelets!ItimmediatelytooktheJSFworldbystorm.KitoMannpublishedthefirstpartofaseriesofarticlesaboutitonJSFCentraltheveryfirstmonth,andRichardHightower

Page 25: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publishedthefamousarticle“FaceletsfitsJSFlikeaglove”severalmonthslater.

Oraclehadnotbeensittingstilleitherin2005,andafterabout16(!)earlyaccessreleasesitannouncedinlate2005attheJavaPolisconferenceinAntwerpen(nowadayscalledDevoxx)thatADFFaceswouldbedonatedtoMyFacesandthusbecomeopensource.

Inthefirstmonthof2006,JacobHookomandAdamWinercontemplatedtheterribleimplementationofJSF’sstatesavemechanism.Thisworkedbyfirstcreatingacomponenttreefromatemplateandthen,neartheendoftherequest,blindlyserializingtheentiretreewithalldatathatmayhavebeenputthereduringtherequest.Duringapostbackthetreeisrestoredfromthisserializedform(hencethenameofthephase“restoreview”).Thisisatremendouswaste,asthemajorityofthisinformationisalreadyavailableinthetemplate.EspeciallywhendoingAJAXrequestswithclient-sidestatesavingthisposesaverybigburden,butitisalsoaproblemwhenstoringthisstateontheserverasitmassivelyincreasesJSF’smemoryusage.Oneofthemainreasonsfordoingstatesavinginsuchterriblewayagainhastodowiththatonedecision:tosupportJSP.WithJSF1.2abouttogofinal,therewasunfortunatelynotimelefttofixthisforversion1.2.

EventhoughitwasclearatthispointthatFaceletswasthefutureofJSF,whenJSF1.2waseventuallyreleasedinMay2006itstillcontainedonlyJSP.Notallwasbadthough.ThankstoacooperationbetweentheJSFandJSPEGs,arevisionofJSPwasreleased,JSP2.1,whichwasmuchbetteralignedwiththedemandsofJSF.Ontopofthat,JSP’sexpressionlanguageandJSF’sexpressionlanguagewere

Page 26: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

merged.TheresultwasUEL(UnifiedExpressionLanguage).AverypracticaladvantageofUEListhatJSFcomponentsnolongerhavetoconvertStringsmanuallyintoexpressionsbutdirectlyreceiveaValueExpressionfromthetemplatinglanguage.BothJSP2.1andJSF1.2becamepartofJavaEE5,whichwasreleasedatthesametime.

OnJune13,2006,theMyFacescommunityannouncedthatthedonatedprojectwouldhaveitsnamechangedtoTrinidad.ADFFaceskeptexistingatOracle,though,butwasbasedonTrinidadwithsomeextrafeatures(suchassupportforPortals,JSR227,etc.).Justtwoweekspriortothat,onMay31,2006,ICEsoftannounceditsfree,althoughstillclosedsource,communityedition.Afewmonthslater,onNovember14,2006,ICEsoftwouldfullyopensourceICEfacesundertheMPLlicense.RichFaces,stillclosedsourceatthatpointandbeingsoldbyExadel,wouldnotstaybehindforlongthough,andsomefourmonthslater,onMarch29,2007,ExadelannouncedapartnershipwithRedHatthatmadeRichFacesavailableunderanopensourcelicenseandavailableandsupportedviaitsJBossgroup.

OntoMaturityOnMay22,2007,thespecificationworkforJSF2.0began.Thescopewashugelyambitiousandpromisednotonlytofixmanyoftheissuesthatpeoplehadbeencomplainingaboutbutalsotointroducequiteabunchofnewfeatures.MentionedamongthemanygoalsintheJSRwasaparticularlyinterestingonewhenlookingatthebiggerpicture—extractingthemanagedbeanfacilityfromJSFandmakingitavailablefortheentireplatform.

Page 27: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Duringthefallof2007thecommunitywaspolledforanamefortheJSFRI.Fournamesrosetothetop,butasisoftenthecasenoneofthesenamescouldbeapprovedbySun’slegaldepartment.EventuallyMojarrawasproposed,andperhapstothesurpriseofsomethisonedidpasslegal’sscrutiny.RyanLubke,oneofthemainJSFcommittersthen,madetheofficialannouncementonDecember5,2007.

Alittleunderayearlater,onOctober29,2008,ÇağatayÇivicistartedanewlibrary,PrimeFaces.ThenamederivesfromÇağatay’snickname,whichisOptimusPrime,thecourageousleaderoftheheroicautobotsinthefictionalTransformersuniverse.ÇağatayhadbeeninvolvedwithJSFdevelopmentforalongtimeandhadworkedontheYUI4JSFJSFcomponentlibrarybefore.PrimeFaceswasinitiallybasedonJSF1.x,butwithJSF2.xloomingandtheprojectstillyoungitwouldsoonafterswitchtoJSF2.x.

OnJuly1,2009,thelong-awaitedJSF2.0finallyarrived.JSF2.0indeedfixednearlyeveryproblemthattheindustryhadwithJSF;finally,Faceletswasincludedasthedefaultviewtemplatinglanguage.JSPwaseffectivelydeprecated.ThestatesavingconcernsthatHookomandWinerbroughtforwardmorethanthreeyearsearlierwereaddressedaswell;fromthenonJSFonlysaveddeltastate(statechanges),andinrestoreviewthecomponenttreewasreloadedfromthetemplate,insteadofactuallyrestored.

AnotherbigconcernbroughtforwardbytheJSFcommunityovertheyears,JSF’sover-the-topemphasisonpostbacks,wasaddressedtoo;GETrequestsbecameafirst-classcitizeninJSF2.0.Awell-knownusabilityproblemwithJSF,sometimescalled“TheTrap,”wasthatforanumberofoperationsthedatainvolvedneededtobethesameduring

Page 28: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

boththeoriginalrequestandthepostback.ThisisnotentirelytrivialtoguaranteeinJSF1.x.JSF2.0introducedtheso-calledviewscopeforthis,whichelegantlysolvedtheproblem.Thecreationofcustomcomponents,yetanotherproblemareaofJSF1.x,wasmademuchsimpleraswell.JSF2.0alsointroducedcoresupportforAJAX,modeledafterthewayAjax4Jsfworked,aresourceAPI,systemevents,andquiteafewotherthings.

OneofJSF2.0’sgoals,makingitsmanagedbeanfacilityusableoutsideJSF,wasimplicitlyreachedbytheCDIspec,whichwasintroducedtogetherwithJSF2.0inJavaEE6.TheCDIspecitselfhasalonghistorytoo,butoneofitsdefiningcharacteristicsisthatCDIBeansarestronglybasedonJSFManagedBeansandareessentiallyasupersetofthose.

Altogethertheimpactofallthosefixesandnewfeatureswassuchthatitsplitthecommunityessentiallyintwo;thosewhohadusedJSF1.xandneverlookedatitagainandthosewhoswitchedtoJSF2.xor,specifically,theoneswhostartedusingJSFwith2.0andneversaw1.x.Thisoftenledtoheateddebates,withthe1.xsidearguingthatJSFishorrible,andthe2.xsidenotunderstandingatallwhythatwouldbethecase.Evenatthetimeofthiswriting,whichisalmostnineyearsafterJSF2.0wasreleased,andalongerperiodthanJSF1.xeverexisted,thesesentimentsstillremaintosomedegree.

DespitethemanythingsthatJSF2.0didright,therewasonemissedopportunity;eventhoughCDIwasnowavailableandsupersededJSF’sManagedBeans,JSFchosenottodeprecateitsmanagedbeanfacilityrightaway.Evenworse,itintroducedanannotation-basedalternativetotheXML-basedsystemJSF1.xusedtodefinemanagedbeans.WithCDIalreadyouttherehavingannotationslike

Page 29: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

javax.enterprise.context.RequestScoped,simultaneouslyintroducingajavax.faces.bean.RequestScopedannotationthatdidexactlythesamethingseemsdebatableasbest.TheEGseemedtobeawareofthisconflict,asawarningwasputinplacethatthesenewannotationswouldpossiblybesupersededbyplatformfunctionalitybeforelong.

OnDecember23,CayHorstmannraisedhisconcernsaboutthisveryunwantedsituationinanarticletitled“[email protected]?”Theresponsewasquiteclear;people,includingJavaEEbookwriterAntonioGoncalves,askedforthishugemistakethatJSF2.0hadmadetobecorrectedassoonaspossibleandtodeprecatejavax.faces.bean.ManagedBeanrightawayintheupcomingJSF2.1maintenancereleasewhichwascalledfor,amongotherthings,torectifyanothermistake(namely,theproblemJSF2.0introducedthatinadditiontoacustomResourceResolveritwasalsonecessarytoprovideacustomExternalContext,whichwasveryunclear).Whyjavax.faces.bean.ManagedBeanindeedwasn’tdeprecatedintheJSF2.1MRremainsamysterytothisday.

WhileapplicationswrittenagainsttheJSF1.xAPIswouldmostlyrununchangedonJSF2.0,oronlyneededafewsmallchanges,thecomponentlibrarieshadamuchhardertime.Specifically,theplatform-providedAJAXsupportmeantthattheexistingcomponentlibrarieswouldhavetoforegotheirownAJAXimplementationsandrebaseonthestandardAPIs.Clearlythatwasnosmallfeat,andittookalongtimeforcomponentlibrariestomigrate,withsomeneverreallymaking

Page 30: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

theswitchatall.HerePrimeFaceswasclearlyatanadvantage.Beinga

relativelynewlibrarywithoutmuchlegacy,itmadetheswitchrelativeeasy.Beitacoincidenceornot,PrimeFaces’ascensioninpopularityseemedtostartrightafterJSF2.0wasreleased,whichwasalsotheexactsametimethatbothICEfacesandRichFacesseemedtobecomelesspopular.Althoughitmustbenotedthathardstatisticsaredifficulttoobtainandcontainmanyfacets(downloads,deployments,book,questionsasked,availablejobs,takingdifferentindustriesintoaccount,etc.),somewherearound2012PrimeFaceshadseeminglybecomethemorepopularJSFcomponentlibrary.

Inthebeginningofthatsameyear,February19,2012,ArjanTijmsandBaukeScholtz(bycoincidencealsotheauthorsofthisbook)startedtheOmniFaceslibraryforJSF.ThegoalofOmniFaceswastobeautilitylibraryforJSF,essentiallywhatApacheCommonsandGoogleGuavaaretoJavaSE.TijmsandScholtzhadworkedonaJSF-basedwebsitetogetherandfoundthattheybothhadacollectionofprivateJSFutilitiesthattheyreusedfordifferentprojects,andalsothatagreatnumberofsimilarutilitieswereessentiallyrewrittenagainandagainformanyJSFprojectsandwerepartiallyfloatingaroundinplaceslikeforummessagesandblogposts.OmniFaceswassetupinparticularnottonotcompetecomponentlibrarieslikePrimeFacesbuttoworktogetherwiththose.Hence,visual-orientedcomponentswerelargelyoutofscopeforOmniFaces.

In2012thespecificationprocessforJSF2.2wasalsoinfullswing.JSF2.2waseventuallyreleasedontheMay21,thenextyear.JSF2.2specificallycameupwithaformalversion

Page 31: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ofthealternativemodeinwhichFaceletscouldoperate;insteadofputtingcomponenttagsonaview,plainHTMLwasputonit,withaspecialIDlinkingthetagtoacomponent.SuchamodeisgenerallyspeakingsomewhatlessinterestingtoJSFdevelopersbutappealsspecificallytowebdesignerswhocanmoreeasilyuseplainHTMLtoolsforsuchviews.JSF2.2alsointroducedaCDIcompatible@ViewScopedannotation,whichremovedoneofthelastreasonstostillusetheJSFmanagedbeanfacilityinJSF2.1,namely,thatinthatversion@ViewScopedonlyworkedonthosebeans.JSF2.2alsointroducedtwonewbigfeatures,FacesFlowandResourceContracts,buttheseseemtohaveseenlittleuptakeinpractice.

JustpriortothestartofJSF2.3,onJuly20,2014,RichFacesleadBrianLeathemannouncedonhisblogthatRichFaces5,thenext-generationversionofRichFaces,wouldbecanceled.Instead,RichFaceswould“pursueapathofstabilityoverinnovation,”whichmeansthatJBosswillmakeRichFaces4.xcompatiblewithJSF2.2andportbackafewthingsthatwereindevelopmentforRichFaces5.Whilethepostwassomewhatoptimistic,itstronglylookedlikethewritingwasonthewallforRichFaces.

OnAugust26,2014,thespecificationworkforJSF2.3started.Anewco-specleadwasintroduced—ManfredRiem,whouptothenhadbeenworkingmostlyontheimplementationsideofMojarra,doingsuchthingsasmigratinghundredsofthetestsforwhichJSFisfamousawayfromtheancientandretiredCactusframeworktoamoremodernMaven-basedone,andmakingsurethegazillionsofopenMojarraissueswerereducedtoamanageablenumber.

Page 32: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

JSF2.3startedoffwithaperhapssomewhatremarkablemessagethatOraclehadonlyafewresourcesavailable.Duringthespecificationprocessthosefewresourcesdroppedtoanumberthatfewwouldhaveexpected—absolutelyzero.Basically,afterJavaOne2015,nearlyallofthespecleadsjustvanishedandmostspecsasaresultabruptlygroundtoahalt.JoshJuneaureportedaboutthisinhisfamousstudy,“JavaEE8,WhatIstheCurrentStatus:CaseStudyforCompletedWorkSinceLate2015,”whichundeniablemakesitclearbyshowinggraphsofe-mails,commits,andissuesresolvedthatOraclehadjustwalkedaway.

TheopennessoftheJSFanditsRIMojarrawerefortunatelysuchthatthespecificationworkandimplementationthereofinMojarracanlargelybecarriedonbytheotherEGmembers,whichindeedhappens.

MeanwhileonFebruary12,2016,RedHatannouncedthatRichFaceswouldbeendoflived(EOL)laterthatyear,namely,inJune2016.OneofthemostpopularJSFcomponentlibrariesatsomepoint,oftennamedsomethinglike“Oneofthebigthree,”effectivelywasnomore.OnJune20,2016,thelastrealcommittotheprojectwasdone,“RF-14279:updateJSDoc.”TwodayslaterRedHatreleasedRichFaces4.5.17andtheGitHubreposwereputintoarchived(readonly)mode.BrianLeathem,whoisstillaJSF2.3EGmember,announcedafewdayslateronFebruary18thathewouldnolongerbedoinganyJSF-relatedwork.

RejuvenationInlate2016theJSFspecleadsbrieflyreturned,butwiththemessagethatthespecmustbecompletedinonlyafewweeks,

Page 33: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

sothe(somewhat)lengthyfinalizationprocesscouldstart.OnMarch28,2017,JSF2.3wastheneventuallyreleased,bringingwithitthestartofreplacingJSFnativeartifactswithCDIversions,andfinallysomethingwhichshouldhavehappenedyearsago:thedeprecationoftheJSFmanagedbeanfacilityinfavorofusingCDIbeans.OtherfeaturesaresupportforWebSocketusingtheJavaEEWebSocketAPIsdonatedbyOmniFaces,theintrospectionofavailableviewresourcesinthesystem,andasearchexpressionframeworkdonatedbyPrimeFaces.

FollowingthesomewhatturbulentdevelopmentoftheJSF2.3specistheevenmoreturbulentannouncementbyOraclein2017thatJavaEE,thusincludingJSF,wouldbetransferredtotheEclipsefoundation.Oraclewouldstopleadingthespecsitownedbefore,whichagainincludesJSF.ThiswouldmeanthatMojarrawouldbere-licensed,andJSFwouldbeevolvedbyanewprocesswithlikelydifferentleads.Atthetimeofwriting,thistransferisinfullswing.

Page 34: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1) (2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_2

2.FromZerotoHelloWorld

BaukeScholtz andArjanTijms

Willemstad,Curaçao Amsterdam,Noord-Holland,TheNetherlands

InthischapteryouwilllearnhowtosetupaJSF(JavaServerFaces)developmentenvironmentwiththeEclipseIDE(integrateddevelopmentenvironment),thePayaraapplicationserver,andH2databasefromscratch.

InstallingJavaSEJDKYouprobablyalreadyknowthatJavaSEisavailableasJREforendusersandasJDKforsoftwaredevelopers.EclipseitselfdoesnotstrictlyrequireaJDKasithasitsowncompiler.JSFbeingasoftwarelibrarydoesnotrequireaJDKtoruneither.Payara,however,doesrequireaJDKtorun,primarilyinordertobeabletocompileJSPfiles,eventhoughJSPhasbeendeprecatedasJSFviewtechnologysinceJSF2.0.

Therefore,youneedtomakesurethatyoualreadyhaveaJDKinstalledasperOracle’sinstructions.ThecurrentJavaSEversionis9,butasJavaEE8wasdesignedforJavaSE8whichiscurrentlymoremature,JDK8isrecommended:https://docs.oracle.com/javase/8/docs/tech

1 2

Page 35: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

notes/guides/install/install_overview.html

.ThemostimportantpartsarethatthePATHenvironment

variablecoversthe/binfoldercontainingtheJavaexecutables(e.g.,"pathto/jdk/bin"),andthattheJAVA_HOMEenvironmentvariableissettotheJDKrootfolder(e.g.,"pathto/jdk").ThisisnotstrictlyrequiredbyJSF,butEclipseandPayaraneedthis.EclipsewillneedthePATHinordertofindtheJavaexecutables.PayarawillneedtheJAVA_HOMEinordertofindtheJDKtools.

WHATABOUTJAVAEE?NotethatyoudonotneedtodownloadandinstallJavaEEfromOracle.comeventhoughJSFitselfispartofJavaEE.JavaEEisbasicallyanabstractspecificationofwhichtheso-calledapplicationserversrepresenttheconcreteimplementations.ExamplesofthoseapplicationserversarePayara,WildFly,TomEE,GlassFish,andLiberty.ItisexactlythoseapplicationserversthatactuallyprovideamongothersJSF(JavaServerFaces),EL(ExpressionLanguage),CDI(ContextsandDependencyInjection),EJB(EnterpriseJavaBeans),JPA(JavaPersistenceAPI),Servlet,WebSocket,andJSON-P(JavaScriptObjectNotationProcessing),APIs(applicationprogramminginterfaces)outofthebox.

Therealsoexistso-calledservletcontainerswhichprovidebasicallyonlytheServlet,JASPIC(JavaAuthenticationServiceProviderInterfaceforContainers),JSP(JavaServerPages),EL,andWebSocketAPIsoutofthebox,suchasTomcatandJetty.However,itwouldrequiresomeworktomanuallyinstallandconfigure,amongothers,JSF,JSTL(JSP

Page 36: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

StandardTagLibrary),CDI,EJB,andJPAonsuchaservletcontainer.ItisnoteventrivialinthecaseofEJBasitrequiresmodifyingtheservletcontainer’sinternals.Thatis,bytheway,exactlywhyTomEEexists.It’saJavaEEapplicationserverbuiltontopofthebarebonesTomcatservletcontainerengine.

ComingbacktotheJavaEEdownloadatOracle.com,itwouldgiveyoubasicallytheGlassFishserver,alongwithabunchofdocumentationandoptionallytheNetbeansIDE.WedonotneeditaswearealreadyusingPayaraastheJavaEEapplicationserver,andaretargetingEclipseasIDE.Therefore,theJavaSEJDKissufficient.

InstallingPayaraPayaraisanopensourceJavaEEapplicationserverwhichisin2014forkedfromGlassFish.ItisbasicallyaresponsetoOracle’sannouncementtostopitscommercialsupportforGlassFish,socompaniespreviouslyusingGlassFishcommerciallycouldeffortlesslyswitchtoPayaraandcontinueenjoyingcommercialsupport.ThankstocommercialsupportforbusinesscustomerspreviouslyusingGlassFish,thePayaraapplicationserversoftwarecancontinuouslybebug-fixedandimproved.

ThefirstPayaraversionwithJSF2.3integratedis5.Youcandownloaditfromhttps://payara.fish.Makesureyouchooseeitherthe“PayaraServerFull”or“PayaraServerWebProfile”downloadandnot,forexample,the“PayaraMicro”or“PayaraEmbedded,”astheyhaveotherpurposes.Installingisbasicallyamatterofunzippingthedownloadedfileandputtingitsomewhereinyourhomefolder.We’llleave

Page 37: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

itthereuntilwehaveEclipseupandrunning,sothatwecanthenintegratePayarainEclipseandletitmanagetheserver.

HOWABOUTOTHERSERVERS?ThechoiceforPayarainthisbookisprimarilybecauseitisattimeofthiswritingoneoftheveryfewavailableJavaEEapplicationserverswithJSF2.3integrated.TheotheroneisGlassFish,butwewouldrathernotadvocateitasitwouldbasicallyoffernocommercialsupportorbugfixes.GlassFishmustbeseenasatruereferenceimplementationforotherapplicationservervendorssotheycan,ifnecessary,buildtheirapplicationserverimplementationbyexample.

WildFly,TomEE,andLibertydidnot,atthetimeofwriting,haveaversionavailablewithJSF2.3integrated.

InstallingEclipseEclipseisanopensourceIDEwritteninJava.Itisbasicallylikenotepadbutwiththousandsifnotmillionsofextrafeatures,suchasautomaticallycompilingclassfiles,buildingaWARfilewiththem,anddeployingittoanapplicationserverwithouttheneedtomanuallyfiddlearoundwithjavacinacommandconsole.

Eclipseisavailableinalotofflavors.Aswe’regoingtodevelopwithJavaEE,weneedtheonesaying“EclipseIDEforJavaEEdevelopers.”It’susuallythetop-rankeddownloadlinkathttp://eclipse.org/downloads/eclipse-packages/.Alsohere,installingisbasicallyamatterofunzippingthedownloadedfileandputtingitsomewhereinyourhomefolder.

Page 38: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

InWindowsandLinuxyou’llfindtheeclipse.iniconfigurationfileintheunzippedfolder.InMacOSthisconfigurationfileislocatedinEclipse.app/Contents/Eclipse.Openthisfileforediting.WewanttoincreasetheallocatedmemoryforEclipse.Atthebottomofeclipse.ini,you’llfindthefollowinglines:-Xms256m-Xmx1024m

Thissets,respectively,theinitialandmaximummemorysizepoolwhichEclipsemayuse.ThisisabittoolowwhenyouwanttodevelopabitofadecentJavaEEapplication.Let’satleastdoubleboththevalues.

-Xms512m

-Xmx2g

Watchoutthatyoudon’tdeclaremorethantheavailablephysicalmemory.Whentheactualmemoryusageexceedstheavailablephysicalmemory,itwillcontinueintovirtualmemory,usuallyinaswapfileondisk.Thiswillgreatlydecreaseperformanceandresultinmajorhiccupsandslowdowns.

NowyoucanstartEclipsebyexecutingtheeclipseexecutableintheunzippedfolder.Youwillbeaskedtoselectadirectoryasworkspace.ThisisthedirectorywhereEclipsewillsaveallworkspaceprojectsandmetadata.

Afterthat,Eclipsewillshowawelcomescreen.Thisisnotinterestingfornow.YoucanclicktheWorkbenchbuttonontherighttoptoclosethewelcomescreen.Untickifnecessary“AlwaysshowWelcomeatstartup”onthebottomright.After

Page 39: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

that,youwillentertheworkbench.Bydefault,itlookslikethescreenshotinFigure2-1.

Figure2-1 Eclipseworkbench

CONFIGURINGECLIPSEBeforewecanstartwritingcode,wewouldliketofine-tuneEclipseabitsothatwedon’teventuallyendupintroubleorwithannoyances.Eclipsehasanenormousamountofsettings,andsomeofitsdefaultvaluesshouldnothavebeenthedefaultvalues.YoucanverifyandconfigurethesettingsviaWindow➤Preferences.

General➤Workspace➤TextfileencodingmustbesettoUTF-8.ParticularlyinWindowsthismightotherwisedefaulttotheproprietary

Page 40: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

encodingCP-1252whichdoesnotsupportanycharactersbeyondtheLatinrange.WhenreadingandsavingUnicodefileswithCP-1252,youriskseeingunintelligiblesequencesofcharacters.Thisisalsocalled“mojibake.”.

General➤Workspace➤NewtextfilelinedelimitermustbesettoUnix.ItworksjustfineonWindowsaswell.Thiswillparticularlykeepversioncontrolsystemshappy.Otherwise,developerspullingcodeondifferentoperatingsystemsmightfaceconfusingconflictsordiffscausedbydifferentlineendings.

General➤Editors➤Texteditors➤Spellingshouldpreferablybedisabled.Thiswillsaveyoufromapotentiallybigannoyance,becauseitunnecessarilyalsospellchecksXMLconfigurationfilessuchasfacesconfig.xmlandweb.xml,causingconfusingerrorsandwarningsinthosefiles.

Java➤Compiler➤Compilercompliancelevelmustbesetto1.8.ThisistheminimumrequiredJavaversionforJavaEE8.

Java➤InstalledJREsmustbesettotheJDK,nottotheJRE.ThissettingwillnormallyalsobeusedtoexecutetheintegratedapplicationserverwhichusuallyrequirestheJDK.

INSTALLINGJBOSSTOOLSPLUG-INStandardEclipseforJavaEEinitscurrentversiondoesnotsupportanyCDItools.IthasnowizardstocreateCDImanagedbeans,orautocompletionandhyperlinkingforCDImanagedbeansinJSFpages.TheJBossToolsplug-inisanextensiveplug-inwhichoffersamongotherstheCDItools.ThisisveryusefulwhendevelopingaJavaEEwebapplication.

Inordertoinstallit,gotoHelp➤EclipseMarketplace.Enterinthesearchfield“JBossTools”andclickGo.ScrollabitthroughtheresultsuntilyouseeJBossToolsFinalandthenclickInstall(seeFigure2-2).

1

2

Page 41: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure2-2 JBossToolsintheEclipseMarketplace

Inthenextstep,you’llseeafairlylargelistofallJBossTools’offerings.Wedon’tneedallofthem.ThelistindeedalsoincludessomeJSF-relatedtools,buttheyarenotterriblyuseful.TheVisualPageEditorisnotatalluseful.DragginganddroppingtogetheraJSFpagedoesn’tmakeyouagoodJSFdeveloper.Thatcanonlybeachievedbyjustwritingcodeyourself.Moreover,havingtoomanyunusedfeaturesinstalledandevenimplicitlyenabledmaymakeEclipseterriblyslow.Thefewerfeaturesyouselect,thelesschancethatyouwillbesurprisedaboutchangesintheIDEbehavior.So,untickthetopcheckboxandthentickonlythecheckboxwhichsays“ContextandDependencyInjectionTools”(seeFigure2-3).

Page 42: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure2-3 SelectonlytheCDItoolsfornow

Next,acceptthetermsofthelicenseagreementandcompletethewizarduntilEclipseisrestarted.

INTEGRATINGNEWSERVERINECLIPSEWeneedtofamilarizeEclipsewithanyinstalledapplicationserverssothatEclipsecanseamlesslylinkitsJavaEEAPIlibrariesintheproject’sbuildpath(read:thecompiletimeclasspathoftheproject).ThisismandatoryinordertobeabletoimportclassesfromtheJavaEEAPIinyourproject.Youknow,theapplicationserveritselfrepresentstheconcreteimplementationoftheabstractJavaEEAPI.

InordertointegrateanewapplicationserverinEclipse,firstcheckthebottomsectionoftheworkbenchwithseveraltabsrepresentingseveralViews(youcanaddnewonesvia

Page 43: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Window➤ShowView).ClicktheServerstabtoopentheserversview(seeFigure2-4).Clickthelinkwhichsays“Noserversareavailable.Clickthislinktocreateanewserver....”

Figure2-4 ServersviewofEclipseWorkbench

Fromthelistofavailableservertools,selectOracle➤GlassFishTools(seeFigure2-5).

Figure2-5 SelectingGlassFishToolsinNewServerwizard

Page 44: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

AfterclickingNextforthefirsttime,itwilldownloadtheplug-ininthebackgroundandrequestyoutoacceptthelicenseagreementbeforeinstallingtheplug-in.Thisplug-inismandatoryinordertomanageanyGlassFish-basedserverfrominsidetheworkbench—amongothers,addingandremovingEclipseprojectstothedeploymentsfolder,startingandstoppingtheserver,andrunningtheserverindebugmode.Onceit’sfinishedinstalling,itwillrequestyoutorestartEclipse.Takeactionaccordingly.

Oncereturnedintotheworkspace,clickthesamelinkintheServersviewagain.You’llnowseeaGlassFish➤GlassFishoption.SelectthisandsettheServernamefieldto“Payara”(seeFigure2-6).

Page 45: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure2-6 SelectingGlassFishserverinNewServerwizardandnamingitPayara

Advancetothenextstep.Here,youshouldpointtheGlassFishlocationfieldtotheglassfishsubfolderofthePayarainstallation,therewhereyouhaveunzippeditafterdownloading(seeFigure2-7).

Page 46: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure2-7 SpecifyingGlassFishlocationinNewServerwizard

CompletetheremainderoftheNewServerwizardwithdefaultsettings.Youdon’tneedtoeditanyotherfields.ThenewlyaddedserverwillnowappearintheServersview(seeFigure2-8).

Figure2-8 ThePayaraserverinServersview

CreatingNewProjectinEclipseWe'renowreadytocreateanewprojectforourJSFapplicationinEclipse.ThiscanbedoneviatheleftsectionoftheworkbenchwhichbydefaultshowsonlyonetabrepresentingtheProjectExplorerview(alsohere,youcanaddnewviewsviaWindow➤ShowView).Right-clickanywhereinthisviewandselectNew➤Project.It’llshowtheNew

Page 47: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Projectwizardwhichmayhaveabittoomanyoptions.Eclipse,beinganIDEformanydifferentprojecttasks,

offersabewilderingamountofdifferentprojecttypesfromwhichtochoose.ForaJavaEE-basedapplicationwhichisgoingtobedeployedasasimpleWARfile,therearebasicallytwoprojecttypesthatwecouldchoosefrom:Web➤DynamicWebProjectandMaven➤MavenProject.

ThedifferenceisthatthefirstisanEclipsenativeprojectthatreallyonlyworksonEclipse,whilethelatterisauniversaltypeofprojectthatcanbebuiltbyanyIDE,aswellaseasilyonthecommandlineandbyvariousCIserverssuchasTravisandJenkins.Forthisreason,theMavenprojecttypeisreallytheonlyviablechoice(seeFigure2-9).

Figure2-9 SelectingMavenProjectinNewProjectwizard(notetheDynamicWebProjectasanotherbutnon-viableoption)

Page 48: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Inthenextstep,makesurethattheoptionCreateasimpleproject(skiparchetypeselection)ischecked(seeFigure2-10).ThiswillletusstartwithareallyemptyMavenprojectsothatwecanconfigureandpopulateitourselves.Ofcourse,youcouldalsochoosefromanarchetype,whichisbasicallyatemplateprojectwithseveralalreadypreparedfilesandconfigurations.Butwedon’tneedanyfornow.

Figure2-10 Checking“Createasimpleproject”inNewMavenProjectwizard

Inthenextstep,wecanspecifyourownMavencoordinatesoftheproject.TheMavencoordinatesconsistof,amongothers,GroupId,ArtifactId,andVersion,alsoknownasGAVintheMavenworld.TheGroupIdusuallymatchestherootpackagenameyou’regoingtouse,suchascom.example.TheArtifactIdusuallyrepresentstheprojectnameyou’regoingtouse.Forsimplicityandinordertobeconsistentintherestofthebook,we’lluseproject.TheVersioncanbekeptdefaultat0.0.1-SNAPSHOT.FinallythePackagingshouldbesettowar.

Page 49: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure2-11 FillingouttheMavenGAVinnewMavenProjectwizard

CompletetheremainderoftheNewMavenProjectwizard(seeFigure2-11).Youdon’tneedtoeditanyotherfields.Onceyou’vefinishedthewizard,you’llgettoseetheprojectstructureintheProjectExplorerview(seeFigure2-12).

Page 50: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure2-12 ThenewlycreatedMavenprojectinEclipse

Unfortunately,theEclipse-generatedpom.xml,whichisthemainindicatoroftheprojectbeingaMavenprojectandcontainingitsconfiguration,islessthanideal.It’snotcurrentanymore,evenwhengeneratedbythelatestEclipse,theOxygen2(December2017).Youcanalreadyseethatbythepom.xmlfilewhichismarkedwithanalarmingredcrossandanerrormessageintheMarkersview.Anyprojectthathasatleastonesuchredcrosscannotbebuiltandwon’tbedeployable.Theerrormessageliterallysays“web.xmlismissingand<failOnMissingWebXml>issettotrue.”Inotherwords,Mavensomehowthinksthatit’sstillapre-JavaEE6project,whenthiswasindeeddisallowed.

Page 51: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

InordertosolvethisproblemandtocatchuptheEclipse-generatedpom.xmlwiththecurrentstandards,weneedtoopenpom.xmlforeditingandadjustitasshowninthefollowingcode:<projectxmlns:="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-

instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.

0

http://maven.apache.org/xsd/maven-4.0.0.xsd"

>

<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>

<artifactId>project</artifactId>

<version>0.0.1-SNAPSHOT</version>

<packaging>war</packaging>

<properties>

<project.build.sourceEncoding>

UTF-8

</project.build.sourceEncoding>

<project.reporting.outputEncoding>

UTF-8

</project.reporting.outputEncoding>

<maven.compiler.source>1.8</maven.compiler.sourc

e>

<maven.compiler.target>1.8</maven.compiler.targe

t>

<failOnMissingWebXml>false</failOnMissingWebXml>

Page 52: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</properties>

<dependencies>

<dependency>

<groupId>javax</groupId>

<artifactId>javaee-api</artifactId>

<version>8.0</version>

<scope>provided</scope>

</dependency>

</dependencies>

</project>

Onceyousavethisfile,Eclipsewillautomaticallysortitoutbyitselfandclearoutthealarmingredcross.Nowthatlooksmuchbetter.We'llbrieflygothroughthemostimportantsettingshere.

Packagingwar—indicatestheprojectisa“web”project,andthattheproject’scontentswillbeassembledintoawebarchive.

EncodingUTF-8—setstheencodingthatthesourcefilesareinandwithwhichthe(reporting)outputfilesshouldbegenerated.Thismakesthebuildrepeatable,asitotherwisewoulddefaulttothesystemdefaultencoding(again,aratherbaddefault).

Compiler1.8—setsboththeversionofJavausedinthe.javasourcefilesaswellasthebytecodeoutputinthe.classfiles.Withoutsettingthis,Mavendefaultstotheoldestversionpossible,andsometimesevenalowerversionthanthat.

failOnMissingWebXmlfalse—olderversionsofJavaEErequiredtheWEB-INFweb.xmltobepresent.EventhoughthishasnotbeenrequiredanymoresinceJavaEE6,whichwasreleasedin2009,Mavenstillchecksforthisfiletobepresent.Settingthistofalsepreventsthisunnecessarycheck.

Dependencyjavax:javaee-api:8.0provided—thisdeclaresadependencyontheJavaEE8API,andmakessurealltheJavaEEtypeslike@Namedareknowntothecompiler.Thisdependencyissettoprovided"sincethosetypesarealreadyprovidedbythetargetruntime,whichisinourcasePayara.

Page 53: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Theywillthenonlybeusedtocompilethesourcecodeagainstandwon’tbeincludedinthegenerated.war.Youneedtomakeabsolutelysurethatanycompiletimedependencywhichisalreadyprovidedbythetargetruntimeissettoprovided;otherwiseitwilleventuallyendupinthegenerated.warandyoumayrunintoclassloadingtroublewhereinduplicatedifferentversionedlibrariesareconflictingwitheachother.Incaseyou’reactuallynottargetingafull-fledgedJavaEEserverbutabarebonesservletcontainer,youwouldneedtoadjustthedependenciesasinstructedintheREADMEofMojarra, oneoftheavailableJSFimplementationsandactuallytheoneusedunderthecoverofPayara.

Now,inEclipse’sMarkersview,there’sonlyonewarningleftwhichsays“BuildpathspecifiesexecutionenvironmentJ2SE-1.5.TherearenoJREsinstalledintheworkspacethatarestrictlycompatiblewiththisenvironment.”Well,thatbasicallymeansthatEclipserecognizesthisMavenprojectasaJava1.5-onlyprojectwhilewedon’tactuallyhaveJavaSE5installed,andinspiteofthecompilerversioninpom.xmlbeingsetto1.8.

InordertotellEclipsethatthisisreallyaJava1.8project,weneedtoright-clicktheprojectinProjectExplorerviewandchooseProperties.IntheProjectFacetssectionyoushouldchangetheversionoftheJavafacetfrom1.5to1.8(or9ifyouhaveJDK9installed)(seeFigure2-13).Whileatit,wealsoneedtoupdatetheServletAPIversionandaddtheCDI,JSF,andJPAfacets.TheServletAPIisrepresentedbythe“DynamicWebModule”entry.Thisneedstobesettoversion4.0,whichmatchesJavaEE8.Furtherthe“CDI,”“JavaServerFaces,”and“JPA”entriesneedtobeselected.The“CDI”facetis,bytheway,onlyavailableafterhavinginstalledtheJBossToolsasinstructedinthesection“InstallingJBossToolsPlug-in.”

Unfortunately,inthelatestavailableEclipseversion,

3

Page 54: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Oxygen2fromDecember2017,thereisn’taJSF2.3orJPA2.2versionavailableyetinthedropdown.ThehighestavailableversionsareJSF2.2andJPA2.1.Thisisnotabigproblem.Itsonlyinfluenceisontheavailablecodegeneratorsandwizards.WecanalwaysadjusttheEclipse-generatedfacesconfig.xmlandpersistence.xmlfilesafterwardtomatchtheJavaEE8compatibleversions.

Figure2-13 TheProjectFacetssectionoftheprojectproperties(notethattheServletAPIversionisrepresentedby“DynamicWebModule”)

Asyoucanseeintheyellowwarningbar,onlyEclipserequiresfurtherconfiguration.ThisconcernsthenewlyselectedJSFandJPAfacets.Whenclickingthelink,wegettoseetheModifyFacetedProjectwizard(seeFigure2-14).

ThefirststepoftheModifyFacetedProjectwizardallows

Page 55: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ustoconfiguretheJPAfacet.WeneedtomakesurethatEclipseisbeinginstructedthattheJPAimplementationisalreadyprovidedbythetargetruntimeandthusEclipsedoesn’tneedtoincludeanylibraries.Thiscanbeachievedbychoosingthe“DisableLibraryConfiguration”optionintheJPAimplementationfield.Aswe’regoingtousethePayara-providedHibernateastheactualJPAimplementation,whichautomaticallysupportsdiscoveringof@Entityannotatedclasses,we’dliketoinstructEclipsetodothesame;otherwiseitwouldautomaticallyaddentitiestothepersistence.xmlwhengoingthroughtheentitycodegenerationwizard,orshowwarningswhenwecreateonemanuallyanddon’taddittothepersistence.xml.

Page 56: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure2-14 TheJPAFacetconfiguration

Notethatconfiguringadatabaseconnectionisnotnecessaryfornowaswe’regoingtouseanembeddeddatabase.

InthenextstepoftheModifyFacetedProjectwizard,wecanconfiguretheJSFcapabilities(seeFigure2-15).Alsohere,

Page 57: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

weneedtomakesurethatEclipseisbeinginstructedthattheJSFimplementationisalreadyprovidedbythetargetruntimeandthusEclipsedoesn’tneedtoincludeanylibraries.Thiscanbeachievedbychoosingthe“DisableLibraryConfiguration”optionintheJSFImplementationLibraryfield.Further,weneedtorenametheservletnameoftheFacesServlettomatchthefictiveinstancevariablename:facesServlet.Lastbutnotleast,weneedtochangetheURLmappingpatternfromtheJurassicfaces*tothemodern*.xhtml.

Figure2-15 TheJSFCapabilitiesconfiguration

Actually,theentireregistrationoftheFacesServletin

Page 58: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

web.xmlis,sinceJSF2.2,notstrictlynecessaryanymore;youcouldevenunchecktheConfigureJSFservletindeploymentdescriptoroptionandrelyonthedefaultauto-registeredmappingsoffaces*,*.faces,*.jsfand*.xhtml.However,asthisallowsendusersandevensearchbotstoopentheverysameJSFpagebydifferentURLs,andthuscausesconfusionamongendusersandduplicatecontentpenaltiesamongsearchbots,we’dbetterrestricttoonlyoneexplicitlyconfiguredURLpattern.

Now,finishandapplyallthewizardsanddialogs.TheJPAplug-inonlyputsthegeneratedpersistence.xmlatthewrongplace.Youneedtomanuallymoveitintosrc/main/resources/META-INF.Figure2-16showsushowtheworkbenchlooksnow.

Page 59: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure2-16 CorrectlyconfiguredJavaEE8MavenprojectinEclipse

WeonlyneedtoadjustallthedeploymentdescriptorstocatchuptotheactuallyusedServlet,JSF,JPA,andCDIversions.ThisisnormallydonebyadjustingtherootelementofthedeploymentdescriptorXMLfiletosetthedesiredXMLschemasandtheversion.

YoucanfindallJavaEE8schemasathttp://xmlns.jcp.org/xml/ns/javaee,whichisanactualwebpagewhichcurrentlyredirectstosomelandingpageatOracle.com.ThismaychangeinthefuturegiventhatJavaEE8iscurrentlyintheprocessofbeingtransferredfromOracletoEclipse.YoucanopenthedeploymentdescriptorXMLfileforeditingbydouble-clickingitandthenselecting

Page 60: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

theSourcetabintheeditor.ThecorrectrootelementdeclarationsforJavaEE8compatibledeploymentdescriptorsarethusasfollows:

src/main/webappWEB-INFweb.xmlforServlet4.0:

<?xmlversion="1.0"encoding="UTF-8"?>

<web-app

xmlns:="http://xmlns.jcp.org/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee

http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"

version="4.0"

>

<!--Servletconfigurationhere.-->

</web-app>

src/main/webappWEB-INFfacesconfig.xmlforJSF2.3:

<?xmlversion="1.0"encoding="UTF-8"?>

<facesconfig

xmlns:="http://xmlns.jcp.org/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee

http://xmlns.jcp.org/xml/ns/javaee/web-

facesconfig_2_3.xsd"

version="2.3"

>

<!--JSFconfigurationhere.-->

</facesconfig>

src/main/resources/META-INF/persistence.xmlforJPA2.2:

<?xmlversion="1.0"encoding="UTF-8"?>

<persistence

xmlns:="http://xmlns.jcp.org/xml/ns/persistence"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persisten

ce

http://xmlns.jcp.org/xml/ns/persistence/persistence_2

_2.xsd"

version="2.2"

>

<!--JPAconfigurationhere.-->

Page 61: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</persistence>

OnlythecurrentlyavailableJPAplug-inofEclipsewillshowanerroronthis.YoucouldignorethisbydisablingtheJPAvalidatorintheproject’sproperties,butyoucanalsojuststepbacktoaJPA2.1compatiblepersistence.xmlforthetimebeing.

Finally,forsakeofcompletenessweneedtocreateonemoredeploymentdescriptorfile,theoneforCDI2.0.Thisisn’tautomaticallygeneratedasit’snotrequired.CDIisbydefaultalwaysenabledinanyJavaEE8compatiblewebapplication.It’sevenmandatoryforthefunctioningofJSF.Amongothersthenew<f:websocket>reliesfullyonCDI.Right-clickthe/WEB-INFfolderoftheprojectandchooseNew➤beans.xmlFile.TheNewbeans.xmlFilewizardwhichappearsnowispartoftheJBossToolsplug-in.Justkeepalloptionsdefaultandfinishthewizard.It’llgeneratethefileasfollows:src/main/webappWEB-INFbeans.xmlforCDI2.0:<?xmlversion="1.0"encoding="UTF-8"?>

<beans

xmlns:="http://xmlns.jcp.org/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-

instance"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/java

ee

http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd

"

version="2.0"bean-discovery-mode="annotated"

>

<!--CDIconfigurationhere.-->

</beans>

Page 62: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

CREATINGTHEBACKINGBEANCLASSWiththeprojectnowcorrectlyconfiguredwecanstartwithdevelopingtheactualMVCapplication.TheControllerpartofMVCisalreadyconfiguredasFacesServletinweb.xml.TheModelpartofMVCiswhatwe’regoingtocreatenow.It’sbasicallyjustasimpleJavaclasswhichisbyJSFconventioncalledaBackingBeansinceit“backs”aView.

Right-clickthesrc/main/javafolderoftheprojectandchooseNew➤Bean.TheNewCDIBeanwizardwhichappearsnowisalsopartoftheJBossToolsplug-in(seeFigure2-17).Inthiswizard,setthePackagetocom.example.project.view,settheNametoHelloWorld,ticktheAdd@Namedcheckbox,andfinallysettheScopeto@RequestScoped.Therestofthefieldscanbekeptdefaultorempty.

Page 63: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure2-17 TheJBossTools-providedNewCDIBeanwizardinEclipse

Theclasseditorwillnowopenwiththenewlycreatedbackingbeanclass.We’llmodifyittogetridoftheuselessconstructor;addtwoproperties,inputandoutput;andaccompanytheinputpropertywithagetterandsetterpair,theoutputpropertywithonlyagetter,andasubmit()actionmethodwhichpreparestheoutputpropertybasedontheinputproperty.Asahint,inEclipseafterenteringthe

Page 64: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

properties,youcanright-clickanywhereintheclasseditorandchooseSource➤GenerateGettersandSetterstohavetheIDEtogeneratethem.Initsentirety,theeditedbackingbeanclassshouldlookasfollows:

packagecom.example.project.view;

importjavax.enterprise.context.RequestScoped;

importjavax.inject.Named;

@Named@RequestScoped

publicclassHelloWorld{

privateStringinput;

privateStringoutput;

publicvoidsubmit(){

output="HelloWorld!Youhavetyped:"+input;

}

publicStringgetInput(){

returninput;

}

publicvoidsetInput(Stringinput){

this.input=input;

}

publicStringgetOutput(){

returnoutput;

}

}

We'llbrieflygothroughtheannotationsthatareusedhere.@Named—givesthebeananame,whichisprimarilyusedtoreferenceitviaEL.Withoutanyattributesthisnamedefaultstothesimpleclassnamewiththefirstletterinlowercase,thus"helloWorld"here.Itwillbeavailableby#{helloWorld}inEL.ThiscanbeusedinJSFpages.

Page 65: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@RequestScoped—givesthebeanascope,whichmeansthesameinstanceofthebeanisusedwithinagivenlifespan.InthiscasethatlifespanisthedurationofanHTTPrequest.Whenthescopeends,thebeanisautomaticallydestroyed.YoucanreadmoreaboutscopesinChapter8.

CREATINGTHEFACELETSFILENext,we'llcreatetheViewpartofMVC.It’sbasicallyjustaXHTMLfilewhichisbyJSFinterpretedasaFaceletsfileorjustFacelet.ThisFaceletsfilewillultimatelygeneratetheHTMLmarkupthatissenttothebrowserinresponsetoarequest.WithhelpofEL,itcanreferenceabeanpropertyandinvokeabeanaction.

Right-clickthewebappfolderoftheprojectandchooseNew➤XHTMLPage(seeFigure2-18).TheNewXHTMLPagewizardwhichappearsnowisalsopartoftheJBossToolsplug-in.Inthiswizard,settheFilenametohello.xhtmlandfinishthewizard.

Page 66: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure2-18 TheJBossTools-providedNewXHTMLPagewizardinEclipse

TheXHTMLeditorwillnowopenwiththenewlycreatedFaceletsfile.You’llalsonoticethatthePaletteviewshowsupinbottombox.ThisisessentiallynotusefulforJSF-basedwebdevelopment.Solet’scloseit.ComingbacktothenewlycreatedFaceletsfile,it’sinitiallyempty.Fillitwiththefollowingcontent:<!DOCTYPEhtml><htmllang="en"

xmlns:="http://www.w3.org/1999/xhtml"

Page 67: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

xmlns:f="http://xmlns.jcp.org/jsf/core"

xmlns:h="http://xmlns.jcp.org/jsf/html"

>

<h:head>

<title>HelloWorld</title>

</h:head>

<h:body>

<h1>HelloWorld</h1>

<h:form>

<h:outputLabelfor="input"value="Input"/>

<h:inputTextid="input"value="#

{helloWorld.input}"/>

<h:commandButtonvalue="Submit"

action="#{helloWorld.submit}">

<f:ajaxexecute="@form"render=":output"

/>

</h:commandButton>

</h:form>

<h:outputTextid="output"value="#

{helloWorld.output}"/>

</h:body>

</html>

We'llbrieflygothroughtheJSF-specificXHTMLtagsthatareusedhere.

<h:head>—generatestheHTML<head>.ItgivesJSFtheopportunitytoautomaticallyincludeanynecessaryJavaScriptfiles,suchastheonecontainingthenecessarylogicfor<f:ajax>.

<h:body>—generatestheHTML<body>.YoucanalsouseaplainHTML<body>inthisspecificFacelet,butthenitdoesn’tgiveanyotherJSFtagtheopportunitytoautomaticallyincludeanynecessaryJavaScriptintheendoftheHTML<body>.

Page 68: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:form>—generatestheHTML<form>.JSFwillautomaticallyincludetheviewstateinahiddeninputfield.

<h:outputLabel>—generatestheHTML<label>.YoucanalsouseaplainHTML<label>inthisspecificFacelet,butthenyou’dhavetomanuallytakecareoffiguringouttheactualIDofthetargetinputelement.

<h:inputText>—generatestheHTML<inputtype="text">.JSFwillautomaticallygetandsetthevalueinthebeanpropertyspecifiedinthevalueattribute.

<h:commandButton>—generatestheHTML<inputtype="submit">.JSFwillautomaticallyinvokethebeanmethodspecifiedintheactionattribute.

<f:ajax>—generatesthenecessaryJavaScriptcodeforAjaxbehavior.Youcanalsodoaswellwithoutit,butthentheformsubmitwon’tbeperformedasynchronously.Theexecuteattributeindicatesthattheentire<h:form>mustbeprocessedonsubmitandtherenderattributeindicatesthatthetagidentifiedbyid="output"mustbeupdatedoncompleteoftheAjaxsubmit.

<h:outputText>—generatestheHTML<span>.ThisistheonebeingupdatedoncompletionoftheAjaxsubmit.Itwillmerelyprintthebeanpropertyspecifiedinthevalueattribute.

ThoseJSF-specificXHTMLtagsarealsocalledJSFComponents.TherewillbemoreonFaceletsfilesandJSFcomponentsintheupcomingchapters.NotethatyoucanalsoperfectlyembedplainvanillaHTMLinaFaceletsfile.JSFcomponentsshouldonlybeusedwhenthefunctionalityrequiresso,oriseasilyachievablewiththem.

DEPLOYINGTHEPROJECTIntheServersview,firststartthePayaraserver.Youcandosobyselectingitandthenclickingthegreenarrowiconwhosetooltipsays“Starttheserver.”Youcan,ofcourse,alsousethebugiconwhosetooltipsays“Starttheserverindebugmode.”TheConsoleviewwillbrieflyshowtheserverstartuplog.

Page 69: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Waituntiltheserverstartsupandhas,intheServersview,gainedthestatusStarted(seeFigure2-19).

Figure2-19 ThePayaraserverinServersviewwiththestatusStarted(notethattheConsoleviewishighlightedasithasunreadserverlogs)

Nowright-clickthePayaraserverentryandchooseAddandRemove.ItwillshowtheAddandRemovewizard(seeFigure2-20)whichgivesyoutheopportunitytoaddandremoveWARprojectstotheserver.Dosoforournewlycreatedprojectandfinishthewizard.

Page 70: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure2-20 TheAddandRemovewizardwhereintheprojecthasbeendeployedtotheserverbymovingittotheright

ItmustbeexplicitlymentionedthatincaseofPayaraandGlassFishserversthisisbesttobedonewhiletheserverisalreadystarted.Whenremovingaprojectwhiletheserverisshutdown,itmaystilllingeraroundintheserver’sdeploymentfolder.That’sjustGlassFish’sownquirk.Forexample,inthecaseofWildFlyandTomcatservers,thisisnotnecessary.

Now,openatabinyourfavoritewebbrowser(seeFigure2-21)andentertheaddress

Page 71: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

http://localhost:8080/project/hello.xhtml

inordertoopenthenewlycreatedJSFpage.

Figure2-21 TheHelloWorldpageinChromebrowserwhereintheinputfieldisfilledwiththetext“somemessage”andthesubmitbuttonhasbeenpressed

ComingbacktotheURL,the"localhost:8080"partisbyconventionthedefaultdomainofanyJavaEEserverwhichisrunningindevelopmentmode.Thesameaddressisalsousedby,amongothers,WildFlyandTomEE.The"/project"partisbyconventionthenameoftheEclipseproject.ThisisinServlettermscalledthe“contextpath”andobtainablebyHttpServletRequest#getContextPath()andinJSFdelegatedbyExternalContext#getRequestContextPath().

Thecontextpathpartcanalsobesettoanemptystring;thedeployedwebapplicationwillthenendupinthedomain

Page 72: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

root.InEclipse,thiscanbesetintheproject’spropertiesaswell.FirstremovetheprojectfromthedeploymentusingtheAddandRemovewizard.Thenright-clicktheproject,chooseProperties,andselectWebProjectSettings.ThensettheContextrootfieldtoaforwardslash“/”andclosetheproperties.Finally,addtheprojectbacktothedeploymentusingAddandRemovewizard.NowitwillbedeployedtothedomainrootandyoucanaccesstheJSFpagebyhttp://localhost:8080/hello.xhtml.Wecanevengetastepfurtherbymakinghello.xhtmlthedefaultlandingfilesothatthisalsodoesn’tneedtobespecifiedintheURL.Thiscanbeachievedbyaddingthefollowingentrytotheweb.xml:<welcome-file-list><welcome-file>hello.xhtml</welcome-file>

</welcome-file-list>

NotethatPayaracanbeconfiguredtoautomaticallypublishchangestothedeploymentwheneveraresourceischangedintheproject.Beforesavingtheeditedweb.xml,double-clickthePayaraserverinServersview,unfoldthePublishingsection,andselectAutomaticallypublishwhenresourcechangealongwithalowintervaloflike0seconds(seeFigure2-22).

Page 73: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure2-22 PayaraserverconfigurationinEclipsewithautomaticpublishingenabledandintervalsetto0seconds

Now,savetheweb.xmlandyou’llnoticethatEclipsewillimmediatelytriggerPayaratopublishthechangeswhilestillrunning.Comingbacktothewebbrowser,you’llnoticethattheJSFpageisnowalsoaccessiblebyjusthttp://localhost:8080(seeFigure2-23).

Page 74: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure2-23 TheHelloWorldpageisnowattheroot

InstallingH2H2 isanin-memorySQLdatabase.It’sanembeddeddatabaseusefulforquicklymodelingandtestingJPAentities,certainlyincombinationwithautogeneratedSQLtablesbasedonJPAentities.AddingH2toyourwebapplicationprojectisamatterofaddingthefollowingdependencytothe<dependencies>sectionofthepom.xml:<dependency><groupId>com.h2database</groupId>

<artifactId>h2</artifactId>

<version>1.4.196</version>

</dependency>

That’sbasicallyit.TheJDBC(JavaDatabaseConnectivity)driverisalreadybuiltin.

CONFIGURINGDATASOURCE

4

Page 75: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

CONFIGURINGDATASOURCEInordertobeabletointeractwithaSQLdatabase,weneedtoconfigureaso-calleddatasourceinthewebapplicationproject.Thiscanbedonebyaddingthefollowingsectiontotheweb.xml:<data-source><name>java:global/DataSourceName</name>

<class-name>org.h2.jdbcx.JdbcDataSource</class-name>

<url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1</url>

</data-source>

ThedatasourcenamerepresentstheJNDI(JavaNamingandDirectoryInterface)name.Theclassnamerepresentsthefullyqualifiednameofthejavax.sql.DataSourceimplementationoftheJDBCdriverbeingused.TheURLrepresentstheJDBCdriver-specificURLformat.ThesyntaxisdependentontheJDBCdriverbeingused.Foranin-memoryH2databasewithadatabasenameof“test,”that’sthusjdbc:h2:mem:test.TheH2-specificDB_CLOSE_DELAY=-1pathparameterbasicallyinstructsitsJDBCdrivernottoautomaticallyshutdownthedatabasewhenithasn’tbeenaccessedforsometime,eventhoughtheapplicationserverisstillrunning.

AconcreteinstanceoftheDataSourcecannowbeinjectedinanyservletcontainermanagedartifactsuchasaservletorfilterasfollows:@ResourceprivateDataSourcedataSource;

YoucouldgetaSQLconnectionfromitviaDataSource#getConnection()fortheplainoldJDBCwork.However,aswe’regoingtouseJavaEE,it’sbettertouseJavaEE’sownJPAforthisinstead.

Page 76: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

CONFIGURINGJPAInordertofamiliarizeJPAwiththenewlyaddeddatasource,weneedtoaddanewpersistenceunittothepersistence.xmlwhichusesthedatasourceasaJTAdatasource.

<persistence-unitname="PersistenceUnitName"transaction-

type="JTA">

<jta-data-source>java:global/DataSourceName</jta-data-

source>

<properties>

<property

name="javax.persistence.schema-

generation.database.action"

value="drop-and-create"/>

</properties>

</persistence-unit>

Yousee,thedatasourceisidentifiedbyitsJNDIname.You’llalsonoticeaJPA-specificjavax.persistence.schema-

generation.database.actionpropertywithavalueof“drop-and-create”whichbasicallymeansthatthewebapplicationshouldautomaticallydropandcreateallSQLtablesbasedonJPAentities.Thisis,ofcourse,onlyusefulforprototypingpurposes,aswe’regoingtodowiththisprojectintherestofthebook.Forreal-worldapplications,you’dbetterpickeither“create”or“none”(whichisthedefault).Thetransactiontypebeingsetto“JTA”basicallymeansthattheapplicationservershouldautomaticallymanagedatabasetransactions.ThiswayeverymethodinvocationonanEJBfromitsclient(usually,aJSFbackingbean)transparently

Page 77: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

startsanewtransactionandwhentheEJBmethodreturnstotheclient(usually,thecallingbackingbean),thetransactionisautomaticallycommittedandflushed.And,anyruntimeexceptionfromanEJBmethodautomaticallyrollsbackthetransaction.

CREATINGTHEJPAENTITYNowwe’regoingtocreateaJPAentityc.Basically,it’saJavaBeanclasswhichrepresentsasinglerecordofadatabasetable.Eachbeanpropertyismappedtoaparticularcolumnofthedatabasetable.Normally,JPAentitiesaremodeledagainstexistingdatabasetables.But,asyou’vereadintheprevioussection,“ConfiguringJPA,”aboutthepersistence.xml,it’salsopossibletodoittheotherwayround:databasetablesaregeneratedbasedonJPAentities.

Right-clickthesrc/main/javafolderoftheprojectandchooseNew➤JPAEntity.Inthewizard,setthePackagetocom.example.project.modelandsettheNametoMessage.Therestofthefieldscanbekeptdefaultorempty(seeFigure2-24).

Page 78: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure2-24 TheNewJPAEntitywizardinEclipse

Modifythenewentityclassasfollows:packagecom.example.project.model;

importjava.io.Serializable;

importjavax.persistence.Column;

importjavax.persistence.Entity;

importjavax.persistence.GeneratedValue;

importjavax.persistence.GenerationType;

importjavax.persistence.Id;

importjavax.persistence.Lob;

importjavax.validation.constraints.NotNull;

@Entity

publicclassMessageimplementsSerializable{

Page 79: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

privatestaticfinallongserialVersionUID=1L;

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

privateLongid;

@Column(nullable=false)@Lob

private@NotNullStringtext;

//Add/generategettersandsetters.

}

Asareminder,youcanletEclipsegenerategettersandsettersbyright-clickinganywhereintheclasseditorandchoosingSource➤GenerateGettersandSetters.

We'llbrieflygothroughtheannotationsthatareusedhere.@Entity—marksthebeanasaJPAentity,sothattheJPAimplementationwillautomaticallycollectdatabase-relatedmetadatabasedonallitsproperties.

@Id@GeneratedValue(strategy=IDENTITY)—marksapropertytobemappedtoadatabasecolumnofSQL“IDENTITY”type.InMySQLterms,that’stheequivalentof“AUTO_INCREMENT”.InPostgreSQLterms,that’stheequivalentof“BIGSERIAL”.

@Column—marksapropertytobemappedtoaregulardatabasecolumn.TheactualdatabasecolumntypedependsontheJavatypebeingused.Withouttheadditional@Lobannotation,that’saVARCHAR(255)whoselengthcanbemanipulatedby@Column(length=n).Withthe@Lobannotation,however,thecolumntypebecomesTEXT.

@Lob—marksaStringpropertytobemappedtoadatabasecolumnoftypeTEXTinsteadofalimitedVARCHAR.

@NotNull—thisisactuallynotpartofJPAbutof“BeanValidation.”Tothepoint,itensuresthatthebeanpropertyisbeingvalidatednevertobenullwhensubmittingaJSFformandwhenpersistingtheJPAentity.(SeeChapter5.)Alsonotethatthisbasicallyreplicatesthe

Page 80: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Column(nullable=false),butthat’sonlybecauseJPAdoesn’tconsideranyBeanValidationannotationsasvaliddatabasemetadatainordertogenerateappropriateSQLtables.

CREATINGTHEEJBSERVICENext,weneedtocreateanEJBinordertobeabletosaveaninstanceoftheaforementionedJPAentityinthedatabase,andtoobtainalistofJPAentities.

Right-clickthesrc/main/javafolderoftheprojectandchooseNew➤Class.Inthewizard,setthePackagetocom.example.project.serviceandsettheNametoMessageService(seeFigure2-25).Therestofthefieldscanbekeptdefaultorempty.

Figure2-25 TheNewJavaClasswizardinEclipse

Modifythenewserviceclassasfollows:

packagecom.example.project.service;

importjava.util.List;

importjavax.ejb.Stateless;

Page 81: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

importjavax.persistence.EntityManager;

importjavax.persistence.PersistenceContext;

@Stateless

publicclassMessageService{

@PersistenceContext

privateEntityManagerentityManager;

publicvoidcreate(Messagemessage){

entityManager.persist(message);

}

publicList<Message>list(){

returnentityManager

.createQuery("FROMMessagem",Message.class)

.getResultList();

}

}

That’sbasicallyit.Let’sbrieflygothroughtheannotations.@Stateless—marksthebeanasastatelessEJBservice,sothattheapplicationserverknowswhetheritshouldpoolthemandwhentostartandstopdatabasetransactions.Thealternativeannotationsare@[email protected]@Statelessdoesnotmeanthatthecontainerwillmakesurethattheclassitselfisstateless.Youasdeveloperarestillresponsibletoensurethattheclassdoesn’tcontainanysharedandmutableinstancevariables.Otherwise,you’dbettermarkitaseither@Statefulor@Singleton,dependingonitspurpose.

@PersistenceContext—basicallyinjectstheJPAentitymanagerfromthepersistenceunitasconfiguredintheproject’spersistence.xml.Theentitymanageris,inturn,responsibleformappingallJPAentitiesagainstaSQLdatabase.Itwill,undercover,doallthehardJDBCwork.

ADJUSTINGTHEHELLOWORLDNowwe’regoingtoadjusttheearliercreatedHelloWorldbackingbeaninordertosavethemessagesinthedatabaseand

Page 82: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

displayalloftheminatable.

@Named@RequestScoped

publicclassHelloWorld{

privateMessagemessage=newMessage();

privateList<Message>messages;

@Inject

privateMessageServicemessageService;

@PostConstruct

publicvoidinit(){

messages=messageService.list();

}

publicvoidsubmit(){

messageService.create(message);

messages.add(message);

}

publicMessagegetMessage(){

returnmessage;

}

publicList<Message>getMessages(){

returnmessages;

}

}

Notethatyoudon’tneedsettersformessageandmessages.We’regoingtousethegettersandsettersoftheMessageentityitself.

Finally,adjustthe<h:body>ofhello.xhtmlasfollows:<h1>HelloWorld</h1><h:form>

<h:outputLabelfor="input"value="Input"/>

Page 83: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:inputTextid="input"value="#

{helloWorld.message.text}"/>

<h:commandButtonvalue="Submit"

action="#{helloWorld.submit}">

<f:ajaxexecute="@form"render=":table"/>

</h:commandButton>

</h:form>

<h:dataTableid="table"value="#{helloWorld.messages}"

var="message">

<h:column>#{message.id}</h:column>

<h:column>#{message.text}</h:column>

</h:dataTable>

Nowreloadthepageinyourfavoritewebbrowserandcreatesomemessages(seeFigure2-26).

Figure2-26 TheHelloWorldusingJSF,CDI,EJB,andJPA

Page 84: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Footnotes1https://en.wikipedia.org/wiki/Mojibake.

2http://tools.jboss.org/features/cdi.html.

3

https://github.com/javaserverfaces/mojarra/blob/master/R

EADME.md.

4http://www.h2database.com.

Page 85: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1)

(2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_3

3.Components

BaukeScholtz andArjanTijms

Willemstad,Curaçao

Amsterdam,Noord-Holland,TheNetherlands

JSF(JavaServerFaces)isacomponent-basedMVC(Model-View-Controller)framework.Inessence,JSFparsestheviewdefinitionintoa“componenttree.”Therootofthistreeisrepresentedbythe“viewroot”instanceassociatedwiththecurrentinstanceofthefacescontext.

UIComponenttree=

FacesContext.getCurrentInstance().getViewRoot();

TheviewisusuallydefinedusingXHTML+XMLmarkupinaFaceletsfile.XMLisamarkuplanguagewhichisverysuitablefordefiningatreehierarchyusingaminimumofcode.ThecomponenttreecanalsobecreatedandmanipulatedusingJavacodeinaJavaclass,butthisgenerallyendsupinveryverbosecodeinordertodeclarevalueormethodexpressionsandtocorrelateparentsandchildrenwitheachother.Frequently,developerswhodosoaren’tawareofhowtaghandlerssuchasJSTL(JavaServerPagesStandardTag

1 2

Page 86: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Library)canbeusedtomanipulatethecomponenttreeusingjustXML.

ThecomponenttreebasicallydefineshowJSFshouldconsumetheHTTPrequestinordertoapplyrequestvaluescomingfrominputcomponents,convertandvalidatethem,updatethemanagedbeanmodelvalues,andinvokethemanagedbeanaction.ItalsodefineshowJSFshouldproducetheHTTPresponsebygeneratingthenecessaryHTMLoutputusingrendererstiedtothecomponentswhoseattributescaninturnreferencemanagedbeanproperties.Inotherwords,thecomponenttreedefineshowthephasesoftheJSFlifecycleshouldbeprocessed.ThediagraminFigure3-1showshowaHTTPpostbackrequestisusuallybeingprocessedbyJSF.

Figure3-1 HowJSFprocessestheHTTPpostbackrequestwithintheMVCarchitecture(thenumbersrepresenttheordering)

Page 87: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Followingisabriefdescriptionofeachstep:1. EndusersendsaHTTPrequestwhichmatchesthemappingofthe

FacesServletandthusinvokesit.

2. TheFacesServletwillbuildthecomponenttreebasedontheFaceletfileidentifiedbytheHTTPrequestpath.

3. Thecomponenttreewillifnecessarygetthecurrentmodelvaluesfromthebackingbeanduringbuildingtheview.AnyattributeofaFaceletstemplatetagandaJSTLcoretagandonlythe"id"and"binding"attributesofaJSFcomponentwillgetexecuted.

4. TheFacesServletwillrestoretheJSFviewstateonthecomponenttree.

5. TheFacesServletwillletthecomponenttreeapplytheHTTPrequestparametersandinputcomponentswillstorethemas“submittedvalue.”

6. Theinputandcommandcomponentswillifnecessarygetthecurrentmodelvaluesfromthebackingbeanduringconsultingthe"rendered","disabled",and"readonly"attributesinordertocheckwhethertheyareallowedtoapplytherequestparameters.

7. ThecommandcomponentswillqueuetheActionEventwhenitdetects,basedonHTTPrequestparameters,thatitwasbeinginvokedintheclientside.

8. TheFacesServletwillletthecomponenttreeprocessallregisteredconvertersandvalidatorsonthesubmittedvaluesandinputcomponentswillstorethenewlyconvertedandvalidatedvalueas“localvalue.”

9. Theinputcomponentswillgettheoldmodelvaluefromthebackingbeanandcomparethemwiththenewvalue.

10. Ifthenewvalueisdifferentfromtheoldmodelvalue,thentheinputcomponentwillqueuetheValueChangeEvent.

11. Whenallconversionandvalidationarefinished,theFacesServletwillinvokethelistenermethodsofanyqueuedValueChangeEventonthebackingbean.

12. TheFacesServletwillletthecomponenttreeupdateallmodelvalues.

13. Theinputcomponentswillsetthenewmodelvaluesinthebackingbean.

14. TheFacesServletwillinvokethelistenermethodsofanyqueuedActionEventonthebackingbean.

15. Thefinalactionmethodofthebackingbeanwillifnecessaryreturnanon-

Page 88: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

nullStringoutcomereferringthetargetview.

16. TheFacesServletwillletthecomponenttreerendertheresponse.

17. ThecomponenttreewillifnecessarygetthecurrentmodelvaluesfromthebackingbeanduringgeneratingtheHTMLoutput.PracticallyanyattributeofaFaceletcomponentandaJSFcomponentwhichisinvolvedingeneratingtheHTMLoutputwillgetexecuted.

18. ThecomponenttreewillwritetheHTMLoutputtotheHTTPresponse.

19. TheFacesServletwillreturntheHTTPresponsetotheenduser.

Thisisdifferentfromarequest-basedMVCframeworkwhereinthedeveloperneedstowritemoreboilerplatecodeinthe“controller”classassociatedwiththeviewinordertodefinewhichrequestparametersneedtobeapplied,and/orhowtheyshouldbeconvertedandvalidatedbeforepopulatingtheentity.Thedeveloperalsooftenneedstomanuallypopulatetheentitybymanuallyinvokingabunchofgettersandsettersbeforepassingtheentitytotheservicelayerwhileinvokingtheaction.ThisallisunnecessaryinJSF.

ItshouldbenotedthatthebackingbeanhasaratheruniquepositionintheMVCparadigm.ItcanactasaModel,aView,andtheController,dependingonthepointofview.ThisisdetailedinChapter8.

StandardHTMLComponentsThedefaultJSFimplementationalreadyprovidesanextensivesetofcomponentsforauthoringHTMLpageswiththehelpofFaceletsviewtechnology.ThoseHTMLcomponentsareavailableunderthehttp://xmlns.jcp.org/jsf/htmlXMLnamespaceURI(UniformResourceIdentifier)whichshouldbeassigned

Page 89: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

tothe"h"XMLnamespaceprefix.

xmlns:h="http://xmlns.jcp.org/jsf/html"

ThemostimportantHTMLcomponentswhichshouldalwaysbepresentinyourJSFpagearethe<h:head>and<h:body>.Withoutthem,JSFwon’tbeabletoauto-includeanyscriptorstylesheetresourcesassociatedwithaparticularcomponent.Forexample,the<h:commandButton>,whichgeneratesaHTMLsubmitbutton,requiresforitsAjaxfunctionalitythejsf.jsscriptfiletobeincludedintheHTMLdocument.

–<h:commandButton>,generatesaHTMLsubmitbutton.

–<h:commandButton>,canoptionallycontainAjaxfunctionality.

–TheAjaxfunctionalityrequiresajsf.jsscriptfileintheHTMLdocument.

Therendererofthatcomponentwillautomaticallytakecareofthat,butthatwouldonlyworkif<h:head>ispresent.The<h:body>isslightlylessimportanthere,buttheremayexistcomponentswhichneedtoaddascripttotheendoftheHTMLbody,suchasthe<f:websocket>.Inotherwords,themostminimalandHTML5-validJSFpagewouldlookasfollows:<!DOCTYPEhtml><htmllang="en"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

>

<h:head>

<title>Title</title>

</h:head>

<h:body>

Page 90: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

...

</h:body>

</html>

ThegeneratedHTMLresponse,asyoucaninspectbyright-clickingViewpagesourceintheaveragewebbrowser,shouldlookasfollows:

<!DOCTYPEhtml>

<htmllang="en"xmlns:="http://www.w3.org/1999/xhtml">

<head>

<title>Title</title>

</head>

<body>

...

</body>

</html>

Yousee,JSFbasicallyreplacesallcomponentsinthepagebytheirgeneratedHTMLoutput.Asdiscussedpreviously,JSFprovidesanextensivesetofstandardHTMLcomponents.Table3-1providesanoverview.

Table3-1 StandardHTMLComponentsProvidedbyJSF

Componenttag

Componentsuperclass

Valuetype

HTMLoutput

Since

<h:body>

UIOutput

-

<body>

2.0

<button

Page 91: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:button>

UIOutcomeTarget

String

<buttononclick=window.location>

2.0

<h:column>

UIColumn

-

<td>(forh:dataTable)

1.0

<h:commandButton>

UICommand

String

<inputtype=submit>

1.0

<h:commandLink>

UICommand

String

<aonclick=form.submit()>

1.0

<h:commandScript>

UICommand

-

<script>(functiontosubmitaform)

2.3

<h:dataTable>

UIData

Object[]

<table>(dynamic)

1.0

<h:doctype>

UIOutput

-

<!DOCTYPE>

2.0

<h:form>

UIForm

-

<formmethod=post>

1.0

<h:graphicImage

UIGraphic

-<imgsrc>

1.

Page 92: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

>

UIGraphic

-

<imgsrc> 0

<h:head>

UIOutput

-

<head>

2.

0

<h:inputFile>

UIInput

Part

<inputtype=file>

2.2

<h:inputHidden>

UIInput

Object

<inputtype=hidden>

1.0

<h:inputSecret>

UIInput

Object

<inputtype=password>

1.0

<h:inputText>

UIInput

Object

<inputtype=text>

1.0

<h:inputTextarea>

UIInput

Object

<textarea>

1.0

<h:link>

UIOutcomeTarget

String

<ahref>

2.0

<h:message>

UIMessage

-

<span>(ifnecessary)

1.0

Page 93: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:messages>

UIMessages

-

<ul>

1.0

<h:messageslayout=table>

UIMessages

-

<table>

1.0

<h:outputFormat>

UIOutput

Object

<span>(ifnecessary)

1.0

<h:outputLabel>

UIOutput

String

<label>

1.0

<h:outputText>

UIOutput

Object

<span>(ifnecessary)

1.0

<h:outputScript>

UIOutput

-

<script>

2.0

<h:outputStylesheet>

UIOutput

-

<linkrel=stylesheet>

2.0

<h:panelGrid>

UIPanel

-

<table>(static)

1.0

<h:panelGroup>

UIPanel

-<span>

1.0

Page 94: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:panelGroup>

UIPanel

-

<span> 0

<h:panelGrouplayout=block>

UIPanel

-

<div>

1.2

<h:selectBooleanCheckbox>

UIInput

Boolean

<inputtype=checkbox>

1.0

<h:selectManyCheckbox>

UIInput

Object[]

<table><inputtype=checkbox>*

1.0

<h:selectManyListbox>

UIInput

Object[]

<selectmultiplesize=n><option>*

1.0

<h:selectManyMenu>

UIInput

Object[]

<selectmultiplesize=1><option>*

1.0

<h:selectOneListbox>

UIInput

Object

<selectsize=n><option>*

1.0

<h:selectOneMenu>

UIInput

Object

<selectsize=1><option>*

1.0

<h:selectOneRadio>

UIInput

Object

<table><inputtype=radio>*

1.0

Page 95: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:selectOneRadiogroup>

UIInput

Object

<inputtype=radioname=group>

2.3

The“Componentsuperclass”columnspecifiesthemostimportantUIComponentsuperclassthecomponentextendsfrom.Youmustinterpretthespecifiedclasstobefromthejavax.faces.componentpackage.

The“Valuetype”columnspecifiesthesupportedtypeofthemodelvaluebehindthecomponent’svalueattribute,ifithasany.IfthevaluetypeisString,itmeansthatonlythemodelvalue’stoString()outcomewillbeusedasvalueofthecomponent,generallyincomponentswhichwouldrenderitassomesortoflabel.Ifit’sObject,itmeansthatitsupportsanykindofvalue,generallyincomponentswhichwouldrenderitastextorparseitasinputvalue,ifnecessarywithhelpofanimplicitorexplicitConverter.IfthevaluetypeisObject[],itmeansthatitrequiresanarrayorcollectionofobjectsasmodelvalue,generallyindataandmulti-selectioninputcomponents,ifnecessarywithanimplicitorexplicitConverter.

Therearetwospecializedinputcomponents.The<h:inputFile>bindstheuploadedfiletoajavax.servlet.http.Partpropertyanddoesn’tsupportoutputtingit—forsecurityreasons—andthe<h:selectBooleanCheckbox>whichbindsthecheckedvaluetoabooleanproperty.Thosetwoinputcomponentsdon’tsupportaConverterandthereforedon’tsupportanyothermodelvaluetype.

The“HTMLoutput”columnspecifiestheminimumgeneratedHTMLoutput.IftheHTMLoutputsays“if

Page 96: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

necessary,”thenitmeansthatthespecifiedHTMLelementisonlyemittedwhenthecomponenthasanyattributespecifiedthatrequiresbeingoutputtedasaHTMLelementattribute,suchasid,styleClass,onclick,etc.Thatis,acomponentcanhaveattributesthatdon’tendupinthegeneratedHTMLoutputatall,suchasbinding,rendered,converter,etc.IfacomponentcanhavemultipleHTMLelementrepresentations,thenthat’susuallycontrolledbythelayoutattributeasyoucanseewith<h:messages>and<h:panelGroup>.IftheHTMLoutputcontains“*”(anasterisk),thenitmeansthatthecomponentmayemitzeroormoreofthespecifiednestedHTMLelements.

The“Since”columnindicatesthefirstJSFversiontheHTMLcomponentwasavailablein.Atthetimethisbookwaswritten,thefollowingJSFversionswereavailable:1.0(March2004),1.1(May2004),1.2(May2006),2.0(July2009),2.1(November2010),2.2(March2013),and2.3(March2017).

TheindividualHTMLcomponentsaredetailedinChapters4and6.

StandardCoreTagsNexttothestandardHTMLcomponents,JSFalsoprovidesasetof“core”tags.Thoseareessentially“helper”tagswhichallowyoutodeclarativelyconfigureoneormoretargetHTMLcomponentsbyeithernestinginthemorwrappingaroundthem.Thosecoretagsareavailableunderthehttp://xmlns.jcp.org/jsf/coreXMLnamespaceURIwhichshouldbeassignedtothe"f"XMLnamespaceprefix.

Page 97: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

xmlns:f="http://xmlns.jcp.org/jsf/core"

Technically,thosetagsareintendedtobereusableonnon-HTMLcomponents.JSFoffersthepossibilityofattachingadifferentrenderkittothecomponenttreewhichdoesn’tgenerateHTMLoutputbutadifferentmarkup—hencethedifferentXMLnamespace.Table3-2providesanoverview.

Table3-2 StandardCoreTagsProvidedbyJSF

Coretag

Creates/handles

Targetcomponent

Since

<f:actionListener>

javax.faces.event.ActionListener

ActionSource

1.0

<f:ajax>

javax.faces.component.behavior.AjaxBehavior

ClientBehaviorHolder(s)

2.0

<f:attribute>

UIComponent#getAttributes()

UIComponent

1.0

<f:attributes>

UIComponent#getAttributes()

UIComponent

2.2

<f:convertDateTime>

javax.faces.convert.DateTimeConverter

(Editable)ValueHolder

1.0

Page 98: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:convertNumber>

javax.faces.convert.NumberConverter

(Editable)ValueHolder

1.0

<f:converter>

javax.faces.convert.Converter

(Editable)ValueHolder

1.0

<f:event>

javax.faces.event.ComponentSystemEvent

UIComponent

2.0

<f:facet>

UIComponent#getFacets()

UIComponent

1.0

<f:importConstants>

javax.faces.component.UIImportConstants

UIViewRoot(metadata)

2.3

<f:loadBundle>

java.util.ResourceBundle

UIViewRoot

1.0

<f:metadata>

javax.faces.view.ViewMetadata

UIViewRoot

2.0

<f:param>

javax.faces.component.UIParameter

UIComponent

1.0

<f:passthroughAttribute>

UIComponent#getPassthroughAttributes()

UIComponent

2.2

Page 99: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:passthroughAttributes>

UIComponent#getPassthroughAttributes()

UIComponent

2.2

<f:phaseListener>

javax.faces.event.PhaseListener

UIViewRoot

1.0

<f:selectItem>

javax.faces.component.UISelectItem

UISelectOne/UISelectMany

1.0

<f:selectItems>

javax.faces.component.UISelectItems

UISelectOne/UISelectMany

1.0

<f:setPropertyActionListener>

javax.faces.event.ActionListener

ActionSource

1.0

<f:subview>

javax.faces.component.NamingContainer

UIComponents

1.0

<f:validateBean>

javax.faces.validator.BeanValidator

UIForm

2.0

<f:validateDoubleRange>

javax.faces.validator.DoubleRangeValidator

EditableValueHolder

1.0

<f:validateLength>

javax.faces.validator.LengthValidator

EditableValueHolder

1.0

Page 100: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:validateLongRange>

javax.faces.validator.LongRangeValidator

EditableValueHolder

1.0

<f:validateRegex>

javax.faces.validator.RegexValidator

EditableValueHolder

2.0

<f:validateRequired>

javax.faces.validator.RequiredValidator

EditableValueHolder

2.0

<f:validateWholeBean>

javax.faces.validator.BeanValidator

UIForm

2.3

<f:validator>

javax.faces.validator.Validator

EditableValueHolder

1.0

<f:valueChangeListener>

javax.faces.event.ValueChangeListener

EditableValueHolder

1.0

<f:view>

javax.faces.component.UIViewRoot

UIComponents

1.0

<f:viewAction>

javax.faces.component.UIViewAction

UIViewRoot(metadata)

2.2

<f:viewParam>

javax.faces.component.UIVi

UIViewRoot

2.0

Page 101: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:viewParam>

javax.faces.component.UIViewParameter

UIViewRoot(metadata)

2.0

<f:websocket>

javax.faces.component.UIWe

bsocket

UIViewRoot

(bodyresource)

2.3

Historically,there’sonemore,the<f:verbatim>,butthiswastargetedtothesinceJSF2.0deprecatedJSP(JavaServerPages)viewtechnologyandishencealsodeprecatedsinceJSF2.0.

The“Creates/handles”columnspecifiesthethingwhichthecoretagcreatesorhandlesonthespecifiedtargetcomponent.

The“Targetcomponent”columnspecifiesthetargetcomponentsuperclassorinterfacesupportedbythecoretag.Youmustinterpretthespecifiedclassorinterfacetobefromthejavax.faces.componentpackage.IfthetargetcomponentisoptionallypluralizedasinUIComponent(s),thenitmeansthatthecoretagcaneitherbenestedinthetargetcomponentorwrappedinoneormoretargetcomponents.IfthetargetcomponentisexplicitlypluralizedasinUIComponents,thenitmeansthatthecoretagcanonlywraponeormoretargetcomponentsandthusnotbenested.

Astotargetcomponentinterfaces,theActionSourceinterfaceisimplementedbyUICommandcomponents.TheClientBehaviorHolderinterfaceisimplementedbyUIForm,UIInput,UICommand,UIData,UIOutput,UIPanel,andUIOutcomeTargetcomponents.TheValueHolderinterfaceisimplementedbyUIOutputand

Page 102: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

UIInputcomponents.TheEditableValueHolderinterfaceisimplementedbyUIInputcomponents.BasedonTable3-1youshouldbeabletoderivetheactualHTMLcomponentsfromthem.

The“Since”columnindicatesthefirstJSFversionthecoretagwasavailablein.Atthetimethisbookwaswritten,thefollowingJSFversionswereavailable:1.0(March2004),1.1(May2004),1.2(May2006),2.0(July2009),2.1(November2010),2.2(March2013),and2.3(March2017).

Mostoftheindividualcoretagsaredetailedinseparatechapters.

LifeCycleJSFhasaverywelldefinedlifecycle.Itisbrokendownintosixphases.EachofthosephasesrunstheHTTPrequestthroughthecomponenttree,performsoperationsonit,andfirescomponentsystemevents.Abriefdescriptionwasalreadygivenintheintroductionofthischapter,alongwithadiagram(Figure3-1).Thefollowingsectionsdescribeeachofthephasesofthelifecycle.

RESTOREVIEWPHASE(FIRSTPHASE)FirstcreatetheUIViewRootinstanceandsetitspropertiessuchaslocalefromany<f:view>tag.Thecomponenttreeisatthatmomentstillempty.Onlywhenthecurrentrequestisapostbackrequest,orwhentheviewhasa<f:metadata>withchildren,thenbuildthefullcomponenttreebasedontheviewdefinition.Basically,aspecific

Page 103: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

UIComponentsubclasswillbeinstantiatedbasedonthecomponenttagdefinedintheviewandpopulatedwithallattributesdefinedintheviewandthenUIComponent#setParent()willbeinvoked,passingtheactualparentcomponent.

TheUIComponent#setParent()methodwillfirstcheckifthereisn’talreadyanexistingparent,andifso,itwillfirethePreRemoveFromViewEventontheoldparent.Then,whenthenewparenthasbeenset,andthusthecurrentcomponenthasbecomepartofthecomponenttree,itwillfirethePostAddToViewEventwiththecurrentcomponent.

Ifthecurrentrequestisapostbackrequest,thenitwillrestorethe“viewstate”identifiedbythejavax.faces.ViewStaterequestparameterintothefreshlybuiltcomponenttree.Afterthat,thePostRestoreStateEventisexplicitlyfiredforeachcomponentinthetree,evenwhenthecomponenttreehasactuallynotbeenbuiltorrestored.Inotherwords,evenwhenit’snotapostbackrequest,thateventisfired.You’dbetterreinterpretthateventas“PostRestoreViewPhase”.Incase,duringthePostRestoreStateEvent,you’reactuallyinterestedinwhetherit’sapostbackrequest,youshouldconsulttheFacesContext#isPostback()aswell.

Bytheendofthephase,ifthefullcomponenttreehasactuallynotbeenbuilt,thenimmediatelyadvancetotherenderresponsephase(sixthphase),therebyskippinganyphaseinbetween.

APPLYREQUESTVALUESPHASE

Page 104: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(SECONDPHASE)TheUIComponent#processDecodes()willbeinvokedonUIViewRoot.TheprocessDecodes()methodwillfirstinvokeprocessDecodes()oneachchildandfacetandtheninvokeUIComponent#decode()onitself.Finally,invokeUIViewRoot#broadCastEvents()tofireanyFacesEventqueuedforthecurrentphase.ThedefaultJSFAPI(applicationprogramminginterface)doesn’toffersuchevents,butdeveloperscancreateandqueuetheirown.

Thedefaultimplementationofthedecode()methodwilldelegatetotheRenderer#decode()method.Inthedecode()methodofeitherthecomponentortherenderer,theimplementationhastheopportunitytoextractthesubmittedvaluefromtherequestparameterandsetitasaninternalproperty.FromthestandardHTMLcomponentset,theonlycomponentsthatdothataretheHTMLform-basedcomponentsderivingfromUIForm,UIInput,andUICommand.TheUIFormcomponentwillinvokeUIForm#setSubmitted()withtrue.TheUIInputcomponentwillinvokeUIInput#setSubmittedValue()withtherequestparametervalue.TheUICommandcomponentwillqueuetheActionEventfortheinvokeapplicationphase(fifthphase).

PROCESSVALIDATIONSPHASE(THIRDPHASE)TheUIComponent#processValidators()willbeinvokedonUIViewRoot.TheprocessValidators()

Page 105: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

methodwillbasicallyfirstfirePreValidateEventforthecurrentcomponent,theninvokeprocessValidators()oneachchildandfacet,andtheninvokePostValidateEventforthecurrentcomponent.Finally,itwillinvokeUIViewRoot#broadCastEvents()tofireanyFacesEventqueuedforthecurrentphase,whichisusuallyaninstanceofValueChangeEvent.

FromthestandardHTMLcomponentset,onlyUIInputcomponentsbehavedifferentlyhere.RightbeforecallingprocessValidators()oneachchildandfacet,theywillfirstinvokeUIInput#validate()onitself.Ifthere’sasubmittedvaluesetduringtheapplyrequestvaluesphase(secondphase),thentheywillfirstinvokeConverter#getAsObject()onanyattachedConverter.Whenitdoesn’tthrowConverterException,thentheywillinvokeValidator#validate()onallattachedValidatorinstances,regardlessofwhetheranyofthemhasthrownValidatorException.

WhennoConverterExceptionorValidatorExceptionwasthrown,thenUIInput#setValue()willbeinvokedwiththeconvertedandvalidatedvalueandtheUIInput#isLocalValueSet()flagwillreturntrueandUIInput#setSubmittedValue()willbeinvokedwithnull.

WhenanyConverterExceptionorValidatorExceptionwasthrown,thenUIInput#setValid()willbeinvokedwithfalseandthemessageoftheexceptionwillbeaddedtothefacescontext

Page 106: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

viaFacesContext#addMessage().Finally,whenUIInput#isValid()returnsfalse,thenFacesContext#setValidationFailed()willbeinvokedwithtrue.

Bytheendofthephase,whenFacesContext#isValidationFailed()returnstrue,immediatelyadvancetotherenderresponsephase(sixthphase),therebyskippinganyphaseinbetween.

UPDATEMODELVALUESPHASE(FOURTHPHASE)UIComponent#processUpdates()willbeinvokedonUIViewRoot.TheprocessUpdates()methodwillinturninvoketheprocessUpdates()methodoneachchildandfacet.Finally,itwillinvokeUIViewRoot#broadCastEvents()tofireanyFacesEventqueuedforthecurrentphase.ThedefaultJSFAPIdoesn’toffersuchevents,butdeveloperscancreateandqueuetheirown.

Alsoduringthisphase,fromthestandardHTMLcomponentset,onlyUIInputcomponentshaveahookhere.AftercallingprocessUpdates()oneachchildandfacet,theywillinvokeUIInput#updateModel()onitself.WhenboththeUIInput#isValid()andUIInput#isLocalValueSet()returntrue,theywillinvokethesettermethodbehindthevalueattributewithgetLocalValue()asargumentandimmediatelyinvokeUIInput#setValue()withnullandclearouttheUIInput#isLocalValueSet()flag.

Page 107: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

WhenaRuntimeExceptionisthrownhere,usuallycausedbyabuginthesettermethoditself,itwillinvokeUIInput#setValid()withfalseandqueuetheUpdateModelExceptionandimmediatelyadvancetotherenderresponsephase(sixthphase),therebyskippinganyphaseinbetween.

INVOKEAPPLICATIONPHASE(FIFTHPHASE)TheUIViewRoot#processApplication()willbeinvoked.ThismethodwillinturninvoketheUIViewRoot#broadCastEvents()tofireanyFacesEventqueuedforthecurrentphase,whichisusuallyaninstanceofAjaxBehaviorEventorActionEvent.NotethattheprocessApplication()methodisonlydefinedontheUIViewRootclassanddoesnottraversethecomponenttree.

RENDERRESPONSEPHASE(SIXTHPHASE)Whenthecomponenttreeisstillempty,i.e.,whentherequestisnotapostbackrequest,orwhentheviewhasno<f:metadata>withchildren,orwhenthedeveloperhasinthemeanwhileexplicitlyinvokedFacesContext#setViewRoot()withitsowninstance,thenbuildthefullcomponenttreebasedontheviewdefinition.Whenthecomponenttreeispresent,firstfirethePreRenderViewEventfortheUIViewRoot,theninvokeUIComponent#encodeAll()ontheUIViewRoot,and

Page 108: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

theninvokePostRenderViewEventfortheUIViewRoot.

TheUIComponent#encodeAll()methodwillbasicallyfirstinvokeencodeBegin()onitself,thenifUIComponent#getRendersChildren()returnstrue,itwillinvokeencodeChildren()onitself,orelseinvokeUIComponent#encodeAll()oneachchild,andtheninvokeencodeEnd()onitself.ThisallhappensonlyifUIComponent#isRendered()returnstrue—thatis,whentherenderedattributeofthecomponenttagdoesn’tevaluatetofalse.

ThedefaultimplementationoftheencodeBegin()methodwillfirstfirethePreRenderComponentEventforthecurrentcomponentandthendelegatetoRenderer#encodeBegin().ThedefaultimplementationoftheencodeChildren()methodwilldelegatetoRenderer#encodeChildren().ThedefaultimplementationoftheencodeEnd()methodwilldelegatetoRenderer#encodeEnd().Ifthecomponenthasnorendererattached,thatis,whenUIComponent#getRendererType()returnsnull,thennoHTMLoutputwillberenderedtotheresponse.

IntheencodeBegin()method,thecomponentortherendererimplementationhastheopportunitytowritetheopeningHTMLelementandallofitsattributestotheresponse.IntheencodeChildren()methodthecomponentortherendererimplementationhastheopportunitytodecorateoroverridetherenderingofthechildrenifnecessary.IntheencodeEnd()methodthecomponentortherendererimplementationhastheopportunitytowritethe

Page 109: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

closingHTMLtag.WritingtotheresponsehappenswiththeresponsewriterasavailablebyFacesContext#getResponseWriter().

ForanymentionedXxxEventclasswhichhasbeenfiredinanyphase,ifanylistenermethodthrowsjavax.faces.event.AbortProcessingException

, thenthecurrentlyrunningphasewillbeimmediatelyabortedandthelifecyclewillimmediatelyadvancetotherenderresponsephase(sixthphase),therebyskippinganyphaseinbetween.

AjaxLifeCycleThelifecycleisalmostidenticalduringAjaxrequests.Onlythesecond,third,fourth,andsixthphasesareslightlydifferent.TheprocessDecodes(),processValidators(),andprocessUpdates()methodswillonlybeinvokedontheUIViewRootitselfandanycomponentcoveredbythecomponentsearchexpressionspecifiedin<f:ajaxexecute>.And,theencodeAll()methodwillonlybeinvokedontheUIViewRootitselfandanycomponentcoveredbythecomponentsearchexpressionspecifiedin<f:ajaxrender>.ReadmoreonsearchexpressionsinChapter12.

NotethusthattherewouldbenodifferenceintheAjaxlifecyclewhenthecomponentsearchexpressioncontainsthe"@all"keyword.Inotherwords,use"@all"withcare.Therearenosensiblereal-worldusecasesfor<f:ajaxexecute="@all">.OntheHTMLside,it’snotpossibletosubmitmultipleformsatonce.Onlytheenclosingformis

1

Page 110: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

submitted.Thebiggestvalueisthus<f:ajaxexecute="@form">.However,thereisonesensiblereal-worldusecasefor<f:ajaxrender="@all">,namely,renderingafullerrorpageincaseanexceptionisthrownduringanAjaxrequest.Eventhen,thiscanonlybeprogrammaticallytriggeredviaPartialViewContext#setRenderAll().Formoredetail,seeChapter9.

ViewBuildTimeThe“viewbuildtime”isnottiedtoaparticularphaseoftheJSFlifecycle.TheviewbuildtimeisthatmomentwhenthephysicalUIViewRootinstanceispopulatedwithallofitschildrenbasedontheviewdefinition.

WhenJSFisabouttocreateanUIComponentinstancebasedontheviewdefinition,itwillfirstcheckwhetherthebindingattributeofthecomponentrepresentationreturnsaconcreteUIComponentinstanceand,ifso,thencontinueusingitinstead,orelsecreatetheUIComponentinstancebasedonthe“componenttype”associatedwithitandtheninvokethesetterbehindthebindingattribute,ifany,withit.Iftheidattributeofthecomponentrepresentationintheviewdefinitionisspecified,thenUIComponent#setId()willbeinvokedwithit.Finally,UIComponent#setParent()willbeinvokedwiththeparentcomponentandthenthecomponentinstancebecomesphysicallypartofthecomponenttree.Thistreewillexistuntiltheendoftherenderresponsephase(sixthphase).Thenitbecomeseligibleforthegarbagecollector,alongwiththereleasedfacescontextinstance.

Page 111: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Effectively,UIComponentinstancesarethusrequestscoped.Thebindingattributecanreferamanagedbeanproperty,butasUIComponentinstancesareinherentlyrequestscoped,thetargetmanagedbeanmustberequestscopedandmaynotbeinabroaderscope.Thiswon’tbecheckedbytheJSFAPI,soyouasthedevelopershouldmakeabsolutelysurethatyoudon’treferenceabroaderscopedmanagedbeaninthebindingattributeofanycomponent.

However,whenthebindingattributereferencesamanagedbeaninabroaderscopethantherequestscope,thenyou’renotonlybasicallysavingtheentirecomponenttreeintheHTTPsessionincasethebeanisvieworsessionscoped,butyou’realsoessentiallysharingtheentirecomponenttreeacrossmultipleHTTPrequestswhichareconcurrentlyaccessingtheverysamemanagedbeaninstance—veryinefficientandthusdangerous.

TheviewbuildtimecantechnicallyhappenduringanyJSFlifecyclephase.Generally,that’stherestoreviewphase(firstphase),particularlyduringapostbackrequest,orwhentheviewhas<f:metadata>withchildren.Itcanalsohappenduringtherenderresponsephase(sixthphase),particularlyduringaGETrequestwhentheviewhasno<f:metadata>withchildren,orwhenanon-redirectnavigationhastakenplaceduringapostback.ItwillalsohappenwhenthedeveloperprogrammaticallyinvokesViewDeclarationLanguage#buildView(),whichcanbeimplicitlydonevia,amongothers,ViewHandler#createView()asshowninthefollowingactionmethodcodeexamplewhichforcesustofullyrebuildthecurrentviewfromscratch:publicvoid

Page 112: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

rebuildCurrentView(){

FacesContextcontext=

FacesContext.getCurrentInstance();

UIViewRootcurrentView=context.getViewRoot();

StringviewId=currentView.getViewId();

ViewHandlerviewHandler=

context.getApplication.getViewHandler();

UIViewRootnewView=viewHandler.createView(context,

viewId);

context.setViewRoot(newView);

}

Donotethattheviewstateisnotperdefinitionduringtheviewbuildtimerestoredintothecomponenttree.Theviewstateisonlyrestoredintothecomponenttreeduringtherestoreviewphase(firstphase),andthathappensafterithasexecutedtheviewbuildtimebyitself.Inotherwords,theaboveshownrebuildCurrentView()methoddoesnotrestorethecurrentviewstateintothenewlycreatedcomponenttree.Programmaticallyrestoringtheviewstateisgenerallynotrecommendedwhenprogrammaticallyrebuildingtheviewasabove,asinareal-worldJSFapplicationthesolereasontorebuildthecurrentviewisusuallytogetridofanychangescausedbythepersistentviewstate,and/ortore-executeanyJSTLtagsbasedonthefreshlychangedvaluesinthemanagedbean.

ViewRenderTimeThe“viewrendertime”isalsonottiedtoaparticularphaseoftheJSFlifecycle.Theviewrendertimeisthatmomentwhen

Page 113: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

UIComponent#encodeAll()ofaparticularcomponentisinvoked.

True,it’sbydefaultalwaysexecutedontheUIViewRootduringtherenderresponsephase(sixthphase),butthisdoesn’tstopyoufromprogrammaticallyinvokingitduringadifferentphase,suchastheinvokeapplicationphase(fifthphase),forexample,inordertoobtainthegeneratedHTMLoutputofanarbitrarycomponentasaStringvariable.

ViewStateAsexplainedinthesection“ViewBuildTime,”theUIComponentinstancesresemblingthecomponenttreeareinherentlyrequestscoped.Theyarecreatedduringtheviewbuildtimeandtheyaredestroyedrightaftertherenderresponsephase(sixthphase).AnychangestopropertiesoftheUIComponentinstances,whicharenotreferencedbyanEL(ExpressionLanguage)expression,andaredifferentfromthedefaultvalues,willbesavedas“viewstate.”Inotherwords,the“viewstate”isverydefinitelynotthesameasthe“componenttree.”Moreover,iftheentirecomponenttreeitselfwouldbesavedintheviewstate,thenthiswouldresultinnotonlyanunnecessarilybloatedviewstatebutalsobadapplicationbehaviorasUIComponentinstancesareinherentlynotthreadsafeandmaythereforeabsolutelynotbesharedacrossmultipleHTTPrequests.

Savingtheviewstatehappensduringtheviewrendertime.ThereinJSFwillwriteouttheviewstatetoajavax.faces.ViewStatehiddeninputfieldofthegeneratedHTMLrepresentationofeveryJSFform.WhentheJSFstatesavingmethodhasthedefaultsetting“server,”then

Page 114: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

thehiddeninputvaluerepresentsauniqueidentifierreferringtheserializedviewstateobjectwhichisstoredintheHTTPsession.WhentheJSFstatesavingmethodisexplicitlysetto“client”usingthefollowingweb.xmlcontextparameter,thenthehiddeninputvalueitselfrepresentstheencryptedformoftheserializedviewstateobject.

<context-param>

<param-name>javax.faces.STATE_SAVING_METHOD</param-name>

<param-value>client</param-value>

</context-param>

<env-entry>

<env-entry-name>jsf/ClientSideSecretKey</env-entry-name>

<env-entry-type>java.lang.String</env-entry-type>

<env-entry-value>[AESkeyinBase64format]</env-entry-

value>

</env-entry>

Notethatexplicitlyspecifyingthejsf/ClientSideSecretKeyenvironmententrywithafixedAES(AdvancedEncryptionStandard)keyismandatoryincaseyou’rerunningtheJSFapplicationonaclusterofservers(“cloud”),orwhenyou’dliketheviewstatestilltobevalidafteraserverrestart.YoucangenerateaBase64-encodedAESkeyyourselfusingthefollowingplainJavasnippet:KeyGeneratorkeyGen=KeyGenerator.getInstance("AES");

keyGen.init(256);//Or128incaseyoudon'thaveJCE.

byte[]rawKey=keyGen.generateKey().getEncoded();

Stringkey=Base64.getEncoder().encodeToString(rawKey);

System.out.println(key);//PrintsAESkeyinBase64

format.

Page 115: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ThestandardJSFform,asrepresentedby<h:form>,submitsbydefaultusingthePOSTmethodtotheverysamerequestedURIaswheretheJSFpagecontainingtheformisbeingrequested.Inotherwords,whenyourequestaJSFpagebyhttp://example.com/project/page.xhtml,thenitwillsubmittotheverysamehttp://example.com/project/page.xhtmlURI.Thisisinwebdevelopmenttermsknownas“postback.”WhenJSFneedstoprocessanincomingpostbackrequest,thentherestoreviewphase(firstphase)will,aftertheviewbuildtime,extracttheviewstatefromthejavax.faces.ViewStateparameterandrestoreallchangedpropertiesintothenewlycreatedUIComponentinstancesofthecurrentrequestsothatthecomponenttreeultimatelyreflectsexactlythesamestateasithadduringtheviewrendertimeofthepreviousrequest.

OnanaverageJSFwebapplication,themajorityofthesavedviewstateisrepresentedbyinternalpropertiesofUIComponentinstancesimplementingthejavax.faces.component.EditableValueHolder

interface, whichcoversallUIInputcomponentssuchas<h:inputText>.WhensubmittingaJSFformfailswithaconversionorvalidationerror,thenallchanged“isvalid?”statesand“localvalue”states,whichcanbeeitherthesubmittedstringvalueorthealreadyconvertedandvalidatedvalue,willforallinvolvedUIInputcomponentsbesavedintheviewstate.Thishasthemajoradvantagethatthatthedeveloperdoesn’tneedtoworryaboutmanuallykeepingtrackoftheminordertorepresentthesubmittedformwithallvalidandinvalidvaluespreservedtothewebsiteuserwhile

2

Page 116: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

keepingthemodel(themanagedbeanproperties)completelyfreeofthosevalues.ThisisamajorusabilityadvantageforboththeJSFdeveloperandthewebsiteuser.

Theminorityofthesavedviewstateisrepresentedbyprogrammaticchangestothecomponenttreehierarchy,ortocomponentattributes.Amongothers,anyprogrammaticchangestoreadonly,disabled,andrenderedattributesaretrackedintheviewstatesothatahackerdoesn’thaveanychancetospooftherequestinsuchwaythatthoseattributesfliptothewrongsidesothatthehackercoulddopotentiallyhazardousthings.Thisisamajorsecurityadvantage.

ViewScopeTheServletAPI,whichJSF,amongothers,isbuiltontopof,offersthreewell-definedscopes:therequestscope,thesessionscope,andtheapplicationscope.Basically,therequestscopeisestablishedbystoringtheobjectofinterestasanattributeoftheHttpServletRequest.Equivalently,thesessionscopeisestablishedbystoringtheobjectofinterestasanattributeoftheHttpSessionandtheapplicationscopeisestablishedbystoringtheobjectofinterestasanattributeoftheServletContext.

JSFaddsonemorescopetothis,theviewscope.Thismustnotbeconfusedwiththecomponenttreeitself.Thecomponenttree(thephysicalUIViewRootinstance)iscreatedanddestroyedduringtheverysameHTTPrequestandisthereforeclearlyrequestscoped.Thismustalsonotbeconfusedwiththeviewstate,althoughtheyarecloselyrelated.

WhentheenduserfiresapostbackrequestonaJSFform,

Page 117: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

andtheapplicationdoesn’tperformanykindofnavigation(i.e.,theactionmethodreturnsnullorvoid),thentheviewstateidentifierwillstaythesameandtheviewscopewillbeprolongedtothenextpostbackrequest,untiltheapplicationperformsanexplicitnavigation,orwhentheHTTPsessionexpires.YoucanestablishtheviewscopebystoringtheobjectofinterestasanentryofUIViewRoot#getViewMap()[email protected],thismapisnotinturnstoredintheviewstate,notevenwhentheJSFstatesavingmethodisexplicitlysetto“client.”TheviewscopeisstoredintheHTTPsession,separatefromtheviewstate.Onlytheviewscopeidentifierisstoredintheviewstate.OnlythechangedattributesoftheUIViewRootinstancearestoredintheviewstate.

PhaseEventsThejavax.faces.event.PhaseListenerinterfacecanbeusedtolistenonanyphaseoftheJSFlifecycle.Thisinterfacedefinesthreemethods:getPhaseId(),whichshouldreturnthephaseyou’reinterestedin;beforePhase(),whichwillbeinvokedrightbeforethespecifiedphaseisexecuted;andafterPhase(),whichwillbeinvokedrightafterthespecifiedphaseisexecuted.InthebeforePhase()andafterPhase()methodsyouthushavetheopportunitytorunsomecodebeforeorafterthephasespecifiedbygetPhaseId().

Thejavax.faces.event.PhaseIdclass definesasetofpublicconstants.ItstilldatesfromJSF1.0whichwasreleasedonlyafewmonthsbeforeJava1.5andhencewastoo

3

4

Page 118: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

lateinthegameinordertobecomearealenum.Theconstantsarelistedbelowwiththeirordinalvalues.

PhaseId.ANY_PHASE(0)

PhaseId.RESTORE_VIEW(1)

PhaseId.APPLY_REQUEST_VALUES(2)

PhaseId.PROCESS_VALIDATIONS(3)

PhaseId.UPDATE_MODEL_VALUES(4)

PhaseId.INVOKE_APPLICATION(5)

PhaseId.RENDER_RESPONSE(6)

Phaselistenerinstancescanberegisteredinvariousways.Declaratively,theycanberegisteredapplication-wideviafaces-config.xml.

<lifecycle>

<phase-listener>com.example.project.YourListener</phase-

listener>

</lifecycle>

Orview-widevia<f:phaseListener>tagenclosedin<f:view>.

<f:view>

<f:phaseListenertype="com.example.project.YourListener"

/>

...

</f:view>

Programmatically,theycanbeaddedandremovedapplication-wideviatheaddPhaseListener()andremovePhaseListener()methodsofjavax.faces.lifecycle.Lifecycleinstance.5

Page 119: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

However,obtainingthecurrentLifecycleinstanceisslightlyconvolutedasthere’snodirectgettermethodforthatinthepublicJSFAPI(yet).

FacesContextcontext=FacesContext.getCurrentInstance();

StringlifecycleId=context.getExternalContext()

.getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);

if(lifecycleId==null){

lifecycleId=LifecycleFactory.DEFAULT_LIFECYCLE;

}

LifecycleFactorylifecycleFactory=(LifecycleFactory)

FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY)

;

Lifecyclelifecycle=

lifecycleFactory.getLifecycle(lifecycleId);

Andtheycanbeaddedandremovedview-wideviatheaddPhaseListener()andremovePhaseListener()methodsofUIViewRoot.AconcreteexampleofaPhaseListenerisgiveninthesection“CustomComponentSystemEvents.”

ComponentSystemEventsAsnotedinthesection“LifeCycle,”abunchofcomponentsystemeventsarefiredduringthelifecycle.Theyextendfromthejavax.faces.event.ComponentSystemEventabstractclass. Insummary,thoseare

PreRemoveFromViewEvent:firedwhenacomponentisabouttoberemovedfromthecomponenttree.

PostAddToViewEvent:firedwhenacomponenthasbeenaddedtothecomponenttree.

PostRestoreStateEvent(read:"PostRestoreViewEvent"):firedforeachcomponentwhentherestoreviewphaseends.Notethatthiseventis

6

Page 120: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

onlyfiredforUIViewRootwhentheviewbuildtimehasn’tyettakenplaceduringthisphase.Iftheviewbuildtimehastakenplaceduringthisphase,thenthiseventisfiredforanycomponentinthetree.

PreValidateEvent:firedwhenacomponentisabouttoprocessitsconverterandvalidators,andalsowhenthereareactuallynone.

PostValidateEvent:firedwhenacomponentisfinishedprocessingitsconverterandvalidators,andalsowhenthereareactuallynone.

PreRenderViewEvent:firedwhentheUIViewRootisabouttowriteHTMLoutputtotheHTTPresponse.NotethatthisisthelatestpossiblesafemomenttochangethedestinationoftheHTTPresponse,ortoprogrammaticallymanipulatethecomponenttree.Whendoingsoafterthismoment,there’snoguaranteethatanyprogrammaticchangestotheresponseorthecomponenttreewilltakeplaceasintended,becausebythentheresponsemayalreadybecommitted,ortheviewstatemayalreadybesaved.

PreRenderComponentEvent:firedwhenacomponentisabouttowriteitsHTMLoutputtotheHTTPresponse.

PostRenderViewEvent:firedwhentheUIViewRootisfinishedwritingtheHTMLoutputtotheHTTPresponse.NotethatthiseventisnewsinceJSF2.3.AllothersarefromJSF2.0.

Therearetwomorecomponentsystemeventsthatwereleftunmentionedinthesection“LifeCycle.”

PostConstructViewMapEvent:firedwhentheUIViewRoothasjuststartedtheviewscope.

PreDestroyViewMapEvent:firedwhentheUIViewRootisabouttodestroytheviewscope.

Thesetwoarenotstrictlytiedtothesix-phasecomponent-basedlifecycleandcanhappenbasicallyanytimeduringthelifecycle.ThePostConstructViewMapEventisfiredwhentheapplicationinvokesUIViewRoot#getViewMap()forthefirsttime.Bydefault,thishappensonlywhenthefirst@ViewScopedmanagedbeanofthecurrentviewstatehasbeencreated.ThePreDestroyViewMapEventisfiredwhentheapplication

Page 121: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

invokesMap#clear()ontheUIViewRoot#getViewMap(),whichusuallyonlyhappenswhenFacesContext#setViewRoot()isinvokedwhilethereisalreadyanexistinginstanceset.Thiswillendtheviewscopeanddestroyanyactive@ViewScopedmanagedbean.Normally,thishappensonlywhentheactionmethodhasreturnedanon-nullnavigationoutcome.

Youcanlistenonanyofthosecomponentsystemeventsusingthejavax.faces.event.ComponentSystemEventList

enerinterface. IntheJSFAPI,theUIComponentclassitselfalreadyimplementsComponentSystemEventListener.ThisinterfaceprovidesaprocessEvent()methodwithaComponentSystemEventargumentwhichinturnhasamongothersagetComponent()methodreturningtheconcreteUIComponentinstancetheeventwasfiredon.ThedefaultimplementationofUIComponent#processEvent()basicallychecksifthecurrenteventisaninstanceofPostRestoreStateEventandifthebindingattributeisspecified,andifso,theninvokesthesettermethodwiththecomponentinstanceitselfasargument.

Therearethreewaystosubscribelistenerstothosecomponentsystemevents.Thefirstistodeclarativelyusethe<f:event>tagintheview.Thiscanbeattachedtoanycomponenttag.Oneexampleyou’llseeinrelativelyalotofJSF2.0/2.1targetedresourcesisthefollowing:

7

Page 122: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:metadata>

<f:viewParamname="id"value="#{bean.id}"/>

<f:eventtype="preRenderView"listener="#{bean.onload()}"

/>

</f:metadata>

whereintheonload()methodisoftenimplementedasfollows:

publicvoidonload(){

FacesContextcontext=FacesContext.getCurrentInstance();

if(!context.isPostback()&&

!context.isValidationFailed()){

//...

}

}

Notethatthe<f:eventlistener="#{bean.onload}">bydefaultexpectsamethodwithComponentSystemEventargument,butifyoudon’tneedit,itcanbeomittedforbrevityandthemethodexpressionshouldbeparenthesized,althoughtheELimplementationmaybeforgivinginthis.

The<f:eventtype="preRenderView">isinessenceawork-aroundinordertobeabletoperformtheinvokeapplicationphaseuponaGETrequestbasedonmodelvaluessetbythe<f:viewParam>.Thiswasneededbecausethe@PostConstructwasunsuitableasitwasinvokeddirectlyafterbean’sconstructionbutfarbeforethemodelvalueswereupdated.SinceJSF2.2withitsnew<f:viewAction>,this<f:event>trickisnotneededanymore:<f:metadata>

Page 123: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:viewParamname="id"value="#{bean.id}"/>

<f:viewActionaction="#{bean.onload}"/>

</f:metadata>

whereintheonload()methodisjustimplementedasfollows:

publicvoidonload(){

//...

}

Anotherreal-worldexampleof<f:event>istohavea@PostConstruct-likebehaviorinthebackingcomponentofacompositecomponentwhereinyoucansafelyperformanynecessaryinitializationbasedonitsattributes.

<cc:interfacecomponentType="someComposite">

...

</cc:interface>

<cc:implementation>

<f:eventtype="postAddToView"listener="#{cc.init()}"/>

...

#{cc.someInitializedValue}

</cc:implementation>

andwhereintheinit()methodoftheSomeCompositeclasslooksasfollows:

privateObjectsomeInitializedValue;//+getter

publicvoidinit(){

Map<String,Object>attributes=getAttributes();

someInitializedValue=initializeItBasedOn(attributes);

}

Page 124: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ThesecondwayistoprogrammaticallyuseUIComponent#subscribeToEvent()inJavacode.Thisallowsyoutoconditionallysubscribeacomponentsystemeventlisteneronanexistingcomponent.Itisimportanttokeepinmindthatacomponentsystemeventlistenerissavedintheviewstate.Inotherwords,it’srestoredinthecomponentinstanceduringtherestoreviewphaseofthesubsequentpostbackrequest.KeepthisinmindwhenusingUIComponent#ubscribeToEvent();otherwiseyoumayendupsubscribingtheverysamelistenermultipletimes.TheJSFimplementationMojarrahasaninternalguardagainstit,providedthattheequals()methodofthelistenerimplementationiscorrectlyimplemented,butMyFacesdoesn’thaveaguardherebecausetheJSFspecificationdoesn’tsayso(yet).

Thisallmakesitalittlecomplicatedtocorrectlyregisteracomponentsystemeventlistenerprogrammaticallyforaspecificcomponent.Ifit’sanexistingcomponent,you’dbetteruse<f:event>instead,orifit’sacustomcomponent,you’dbetteruse@ListenerForannotation,whichisactuallythethirdway.Belowisakickoffexampleofcorrectlyregisteringacomponentsystemeventlistenerprogrammatically,providedthatYourListenerclasshasitsequals()andhashCode()methodscorrectlyimplemented,andthatitimplementsSerializableorExternalizableorjavax.faces.component.StateHoldersothatitcanbesavedcorrectlyinviewstate.

Class<PreRenderViewEvent>event=PreRenderViewEvent.class;

ComponentSystemEventListenerlistener=newYourListener();

Page 125: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

List<SystemEventListener>existingListeners=

component.getListenersForEventClass(event);

if(existingListeners!=null&&

!existingListeners.contains(listener)){

component.subscribeToEvent(event,listener);

}

Yes,thatnullcheckisnecessary.TheUIComponent#getListenersForEventClass()

isn’tspecifiedtoreturnanemptylistinstead.Allinall,thisisclearlynotacarefullythoughtoutAPI.You’dbetteruse<f:event>or@ListenerForinsteadtoavoiddirtycodeandconfusion.

Aspreviouslystated,thethirdwayisdeclarativelytousethe@ListenerForannotation.YoucanputthisannotationonlyonaUIComponentorRendererclass.Youcan’tputthisannotationonabackingbeanclass.Forthat,youshoulduse<f:event>instead.The@ListenerForannotationtakesthetargetevent(s)asvalue.TheconcreteComponentSystemEventListenerinstanceistheUIComponentinstanceitself.IftheannotationisdeclaredonaRendererclass,thenthetargetcomponentistheUIComponentinstancewhoseUIComponent#getRendererType()referstheparticularRendererclass.ThefollowingexampleshowsitforacustomcomponentYourComponent:

@FacesComponent("project.YourComponent")

@ListenerFor(systemEventClass=PostAddToViewEvent.class)

publicclassYourComponentextendsUIComponentBase{

@Override

Page 126: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicvoidprocessEvent(ComponentSystemEventevent){

if(eventinstanceofPostAddToViewEvent){

//...

}

else{

super.processEvent(event);

}

}

//...

}

Yes,thatinstanceofcheckisnecessary.Asnotedinthesection“LifeCycle,”thePostRestoreStateEventisbydefaultexplicitlyfiredforanycomponentinthetree.Thesuper.processEvent(event)callisnecessaryincasethiscomponenthasthebindingattributespecified;thatis,thedefaultUIComponent#processEvent()implementationcallsduringPostRestoreStateEventthesettermethodbehindthebindingattribute.

CustomComponentSystemEventsYoucancreateyourownComponentSystemEventtypes.BasicallyallyouneedtodoistoextendfromtheComponentSystemEventabstractclassanddeclarethe@NamedEventannotationonitandfinallyinvokeApplication#publishEvent()atthedesiredmoment.

Imaginethatyouwanttocreateacustomcomponentsystemeventwhichisfiredbeforetheinvokeapplicationphase(fifthphase),aPreInvokeApplicationEvent.Thecustomeventlooksasfollows:

Page 127: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@NamedEvent(shortName="preInvokeApplication")

publicclassPreInvokeApplicationEventextends

ComponentSystemEvent{

publicPreInvokeApplicationEvent(UIComponentcomponent){

super(component);

}

}

Andhere’showyoucanuseaPhaseListenertopublishit.

publicclassPreInvokeApplicationListenerimplements

PhaseListener{

@Override

publicPhaseIdgetPhaseId(){

returnPhaseId.INVOKE_APPLICATION;

}

@Override

publicvoidbeforePhase(PhaseEventevent){

FacesContextcontext=

FacesContext.getCurrentInstance();

context.getApplication().publishEvent(context,

PreInvokeApplicationEvent.class,

context.getViewRoot());

}

@Override

publicvoidafterPhase(PhaseEventevent){

//NOOP.

}

}

Afterregisteringthisphaselistenerinfaces-config.xml,youcanuse<f:event>or@ListenerFortolistenonthisevent.Onereal-worldexamplewouldbenestedinthe

Page 128: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:view>oramastertemplate,orinaparticular<h:form>,sothatyoudon’tneedtocopy/pastetheverysame<f:actionListener>overmultipleUICommandcomponentsintemplateclientsorforms.

<f:eventtype="preInvokeApplication"

listener="#{bean.prepareInvokeApplication}"/>

JSTLCoreTagsIfyouhaveeverdevelopedwithJSP,thenyou’llmostlikelyhavestumbleduponJSTLtags.InFacelets,however,onlyalimitedsubsetofJSTLtagsisreincarnated.Theyare<c:if>,<c:choose><c:when><c:otherwise>,<c:forEach>,<c:set>,and<c:catch>.Essentially,theXMLnamespaceandtagnamesareidenticaltothosefromJSP,buttheyarecompletelyrewrittenforFacelets.

Thisgroupoftagsisformallycalled“JSTLcoreFaceletstaglibrary”insteadof“JSTLcoreJSPtaglibrary”andisalsodocumentedseparatelyfromJSP. ThoseJSTLtagsareavailableunderthehttp://xmlns.jcp.org/jsp/jstl/coreXMLnamespaceURIwhichshouldbeassignedtothe"c"XMLnamespaceprefix.

xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"

Yes,astonishinglywith"/jsp"pathintheURI.Historically,thepredecessorofFaceletsinJSF2.0hadthoseJSTLtagsalsoimplemented,butitdidn’tusethenamespaceURIasin

8

Page 129: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

themorerecentJSTL1.1specification.Instead,itusedthenamespaceURIasintheJSTL1.0specification:http://java.sun.com/jstl/core.However,thiswas“rectified”forFaceletsinJSF2.0.Inmyhumbleopinion,thisisoutrightconfusingastheJSTL1.1XMLnamespaceURIsuggeststhatthoseareactuallyJSPtagsandnotFaceletstags.Butitiswhatitis.

ThetechnicalreasonbehindtheoriginalchangeoftheJSTLnamespaceURIisthemigrationofELfromJSTLtoJSP.ItwasintroducedinJSTL1.0andworkedonlyinJSTLtagsandthusnotoutsideJSTLtags.JSP2.0wantedtomakeuseofthepotentialofELaswellandsoitwasmigratedfromJSTLtoJSP.JSTL1.1thusshippedwithoutELandwasn’tbackward-compatiblewithJSTL1.0anymore—hencethenamespaceURIchangetodistinguishthis.

JSTLtagshaveadifferentlifecyclethanJSF’sstandardHTMLcomponents.JSTLtagsalreadyrundirectlyduringtheviewbuildtimewhileJSFisbusybuildingthecomponenttreebasedontheviewdefinition.JSTLtagsdon’tendupintheJSFcomponenttree.Inotherwords,youcanuseJSTLtagstocontroltheflowofbuildingoftheJSFcomponenttree.

NotethatusingJSTLtocontrolthecomponenttreebuildingisn’taseasilypossibleasusingJSFonJSPinsteadofFacelets.Thatis,JSTLtagsforJSPcanonlyrecognizeJSP-specific${}expressionsandnotJSF-specific#{}expressions.ThismeansthatJSTLtagsinJSPcan’trecognizeJSFmanagedbeansiftheyaren’tyetcreatedbyJSFatthatmoment,andthatJSFcomponentscan’taccessthevarattributeofa<c:forEach>.InFacelets,theJSTLtagsarethusretrofittedsothattheysupportthe#{}expressions.This

Page 130: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

makesthemverypowerful.WhendevelopingJSFpageswithJSTLtagsthemost

importantthingthatyouneedtokeepinmindisthattheyrunduringtheviewbuildtimeandthattheydon’tparticipateintheJSFlifecycle.BelowIhavedemonstratedthemostimportantdifferencesbetweenaJSTLtaganditsJSF/Faceletscounterpart.

<c:forEach>versus<ui:repeat>

Followingisa<c:forEach>exampleiteratingoveraList<Item>withthreeinstancesofanexampleItementityhavingidandvalueproperties:

<c:forEachitems="#{bean.items}"var="item">

<h:outputTextid="item_#{item.id}"value="#{item.value}"

/>

</c:forEach>

Duringtheviewbuildtime,thiscreatesthreeseparate<h:outputText>componentsinthecomponenttree,roughlyrepresentedasfollows:

<h:outputTextid="item_1"value="#{bean.items[0].value}"/>

<h:outputTextid="item_2"value="#{bean.items[1].value}"/>

<h:outputTextid="item_3"value="#{bean.items[2].value}"/>

Inturn,theyindividuallygeneratetheirHTMLoutputduringviewrendertime,asfollows:

<spanid="item_1">one</span>

<spanid="item_2">two</span>

<spanid="item_3">three</span>

Page 131: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

DonotethattheidattributeofaJSFcomponentisalsoevaluatedduringtheviewbuildtimeandthusyouneedtomanuallyensuretheuniquenessoftheresultingcomponentID.OtherwiseJSFwillthrowanIllegalStateExceptionwithamessagewhichgoeslikethis:“DuplicatecomponentIDfoundinview.”TheonlyotherJSFcomponentattributewhichisalsoevaluatedduringtheviewbuildtimeisthebindingattribute.IfyouabsolutelyneedtobindaJSTL-generatedcomponenttoabackingbeanproperty,whichisrare,thenyoushouldbespecifyingauniquearrayindex,collectionindex,ormapkey.Followingisanexampleprovidedthat#{bean.components}refersanalreadypreparedUIComponent[],List<UIComponent>,orMap<Long,UIComponent>property.

<c:forEachitems="#{bean.items}"var="item">

<h:outputTextbinding="#{bean.components[item.id]}"

id="item_#{item.id}"value="#{item.value}"/>

</c:forEach>

TheFaceletscounterpartofthe<c:forEach>isthe<ui:repeat>.ThisisinessenceaUIComponentwhichdoesn’tgenerateanyHTMLoutputbyitself.Inotherwords,the<ui:repeat>itselfalsoendsupintheJSFcomponenttreeduringtheviewbuildtime,andonlyrunsduringtheviewrendertime.Itbasicallyre-rendersitschildrenduringeveryiterationroundagainstthecurrentlyiterateditemasvarattribute.

Page 132: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<ui:repeatid="items"value="#{bean.items}"var="item">

<h:outputTextid="item"value="#{item.value}"/>

</ui:repeat>

DuringtheviewbuildtimetheaboveendsupexactlyasisintheJSFcomponenttree:asingleUIRepeatinstancewithonenestedHtmlOutputTextinstancewhereasthe<c:forEach>createsherethreeHtmlOutputTextinstances.Then,duringtheviewrendertime,theverysame<h:outputText>componentisbeingreusedtogenerateitsHTMLoutputbasedoncurrentiterationround.

<spanid="items:0:item">one</span>

<spanid="items:1:item">two</span>

<spanid="items:2:item">three</span>

Donotethatthe<ui:repeat>asaNamingContainercomponentalreadyensuredtheuniquenessoftheclientIDbasedontheiterationindex.It'stechnicallyalsonotpossibletoreferenceitsvarattributeintheidattributeofanychildcomponentasthevarattributeisonlysetduringviewrendertimewhiletheidattributeisalreadysetduringviewbuildtime.

<c:if>/<c:choose>versusrendered

Imaginethatwehaveacustomtagfilewhichcanbeusedasfollows:

<t:inputtype="email"id="email"label="Email"value="#

{bean.email}"/>

Page 133: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Andtheinput.xhtmltagfilecontainsthefollowingFaceletsmarkupconditionallyaddingdifferenttagsusinga<c:choose>(youcanalsouse<c:if>forthis):<c:choose>

<c:whentest="#{typeeq'password'}">

<h:inputSecretid="#{id}"label="#{label}"

value="#{value}"/>

</c:when>

<c:whentest="#{typeeq'textarea'}">

<h:inputTextareaid="#{id}"label="#{label}"

value="#{value}"/>

</c:when>

<c:otherwise>

<h:inputTextid="#{id}"label="#{label}"

value="#{value}"

a:type="#{type}">

</h:inputText>

</c:otherwise>

</c:choose>

Notethatamoreelaborateexamplecanbefoundinthesection“TagFiles”inChapter7.Thisconstructwillthenonlycreatethe<h:inputText>componentinthecomponenttree,roughlyrepresentedasfollows:

<h:inputTextid="email"label="Email"value="#{bean.email}"

a:type="email">

</h:inputText>

andwhenusingtherenderedattributeinsteadof<c:choose>asfollows:

Page 134: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:inputSecretid="#{id}_password"rendered="#{typeeq

'password'}"

label="#{label}"value="#{value}">

</h:inputSecret>

<h:inputTextareaid="#{id}_textarea"rendered="#{typeeq

'textarea'}"

label="#{label}"value="#{value}">

</h:inputTextarea>

<h:inputTextid="#{id}_text"

rendered="#{typene'password'andtypene'textarea'}"

label="#{label}"value="#{value}">

</h:inputText>

Thentheywillallendupinthecomponenttreeroughlyasfollows:

<h:inputSecretid="email_password"rendered="#{typeeq

'password'}"

label="Email"value="#{bean.email}">

</h:inputSecret>

<h:inputTextareaid="email_textarea"rendered="#{typeeq

'textarea'}"

label="Email"value="#{bean.email}">

</h:inputTextarea>

<h:inputTextid="email_text"

rendered="#{typene'password'andtypene'textarea'}"

label="Email"value="#{bean.email}">

</h:inputText>

Yousee,thiswillthusendupinanunnecessarilybloatedcomponenttreewithalotofunusedcomponentswhenyouhavemanyofthem,particularlywhenthetypeattributeisactuallystatic(i.e.,itdoesnoteverchange,atleastduringtheviewscope).Alsonotethattheidattributeofeachcomponenthasastaticsuffixsothatyoudon’tendupwith“DuplicatecomponentIDfoundinview”exceptions.

Page 135: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<c:set>versus<ui:param>

Theyarenotinterchangeable.The<c:set>setsavariableintheELscope,whichisaccessibleonlyafterthetaglocationduringtheviewbuildtime,butanywhereelseintheviewduringtheviewrendertime.The<ui:param>shouldonlybenestedin<ui:include>,<ui:decoratetemplate>,or<ui:compositiontemplate>andsetsavariableintheELscopeoftheFaceletstemplate,whichisaccessibleonlyinthetemplateitself.OlderJSFversionshadbugswherebythe<ui:param>variablewasalsoavailableoutsidetheFaceletstemplateinquestion.Thisshouldneverbereliedupon.

The<c:set>withoutascopeattributewillbehavelikeanalias.ItdoesnotcachetheresultoftheELexpressioninanyscope.ItsprimarypurposeistobeabletohaveashortcuttoarelativelylongELexpressionwhichisrepeatedseveraltimesinthesameFaceletsfile.Itcanthusbeusedperfectlywellinside,forexample,iteratingJSFcomponents.

<ui:repeatvalue="#{bean.products}"var="product">

<c:setvar="price"value="#{product.price}"/>

#{price}

</ui:repeat>

It'sonlynotsuitable,forexample,forcalculatingthesuminaloop.Thefollowingconstructwillneverwork:

<c:setvar="total"value="#{0}"/>

<ui:repeatvalue="#{bean.products}"var="product">

<c:setvar="total"value="#{total=total+

product.price}"/>

Page 136: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

#{product.price}

</ui:repeat>

Totalprice:#{total}

Forthat,useEL3.0streamAPIinstead.

<ui:repeatvalue="#{bean.products}"var="product">

#{product.price}

</ui:repeat>

Totalprice:#{bean.products.stream().map(product-

>product.price).sum()}

However,whenyousetthescopeattributewithoneofallowablevaluesrequest,view,session,orapplication,thenitwillbeevaluatedimmediatelyduringtheviewbuildtimeandstoredinthespecifiedscope.

<c:setvar="DEV"

value="#{facesContext.application.projectStageeq

'Development'}"

scope="application"/>

ThiswillbeevaluatedonlyonceduringthefirsttimethisviewisbeingbuiltandavailableasanELvariable#{DEV}throughouttheentireapplication.You’dbestdeclaresuch<c:set>inthemastertemplatefilewhichisusedbyeverysingleFaceletsfileintheentireapplication.NotethattheELvariableiscapitalizedtoconformtoJavanamingconventionsforconstants.

CAVEATS

UsingJSTLtagswillonlyleadtounexpectedresultswhenaJSTLtagattributereferencesanELvariablewhichisnotavailableduringviewbuildtime.ExamplesofsuchELvariablesarethosedefinedbyvarattributeof

Page 137: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

iteratingcomponentssuchas<h:dataTable>and<ui:repeat>,andthosesetinmodelby<f:viewParam>,<f:viewAction>,and<f:eventtype="preRenderView">.

Inanutshell,useJSTLtagsonlytocontrolflowofJSFcomponenttreebuildinganduseJSFUIcomponentsonlytocontrolflowofHTMLoutputgeneration.InJSTLtags,donotrelyonELvariableswhicharenotavailableduringviewbuildtime.

ManipulatingtheComponentTreeThiscanbedonedeclarativelyusingJSTLtagsaswellasprogrammaticallyusingJavacode.TheJSTLapproachhasalreadybeenelaboratedintheprevioussection.It’salsopossibletouseJavacodeinstead.Asaprecaution,thisgenerallyendsupinveryverboseandhard-to-maintaincode.Tree-basedhierarchiesincodearebestreadableandmaintainablewhenusingahierarchicalmarkuplanguagesuchasXML.FaceletsitselfisalreadyXMLbased.JSTLisalsoXMLbasedandthereforeseamlesslyintegratesinaFaceletsfile.JSTListhereforetherecommendedapproachtodynamicallymanipulatethecomponenttree,ratherthanJavacode.

TheJavadocofjavax.faces.component.UIComponent specifieswhenyoucouldsafelymanipulatethecomponenttree:

Dynamicallymodifyingthecomponenttreecanhappenatanytime,duringandafterrestoringtheview,butnotduringstatesavingandneedstofunctionproperlywithrespecttorenderingandstatesaving.

Inotherwords,theearliestmomentwhenyoucanguaranteesafelymodifyingthecomponenttreeisduringthePostAddToViewEventandthelatestmomentwhenyou

9

Page 138: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

canguaranteesafelymodifyingthecomponenttreeisduringthePreRenderViewEvent.Anymomentinbetweenisthusalsopossible.BeforethePostAddToViewEventthere’snotnecessarilyameansofaconcreteUIViewRootinstance.AfterthePreRenderViewEventthere’sariskthatthestateisalreadysavedandyou’drathernotgettrappedhere.Inotherwords,manipulatingthecomponenttreeduringtherenderresponsephase(sixthphase)isabadidea.

WhenyouintendtomanipulatethecomponenttreebymeansofaddingnewcomponentsbasedonaJavamodelwhichisatleastviewscoped,thenlistenonthePostAddToViewEventoftheparentcomponentofinterest.Whenyouintendtomanipulatethecomponenttreebasedonthefullybuiltcomponenttreebymeansofadding/moving/removingcomponents,thenlistenonthePreRenderViewEventoftheUIViewRoot.

ThefollowingexampleprogrammaticallypopulatesadynamicformbasedonaJavamodelduringthePostAddToViewEvent:

<h:formid="dynamicFormId">

<f:eventtype="postAddToView"listener="#

{dynamicForm.populate}"/>

</h:form>

whereinthe#{dynamicForm}lookssomethinglikethefollowing:

@Named@RequestScoped

publicclassDynamicForm{

privatetransientUIFormform;

Page 139: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

privateMap<String,Object>values=newHashMap<>();

@Inject

privateFieldServicefieldService;

publicvoidpopulate(ComponentSystemEventevent){

form=(UIForm)event.getComponent();

List<Field>fields=fieldService.list(form.getId());

fields.forEach(field->field.populate(this));

}

publicvoidcreateOutputLabel(Fieldfield){

HtmlOutputLabellabel=newHtmlOutputLabel();

label.setId(field.getName()+"_l");

label.setFor(field.getName());

label.setValue(field.getLabel());

form.getChildren().add(label);

}

publicvoidcreateInputText(Fieldfield){

HtmlInputTexttext=newHtmlInputText();

text.setId(field.getName());//ExplicitIDis

required!

text.setLabel(field.getLabel());

text.setValueExpression("value",

createValueExpression(field));

form.getChildren().add(text);

}

publicvoidcreateMessage(Fieldfield){

HtmlMessagemessage=newHtmlMessage();

message.setId(field.getName()+"_m");

message.setFor(field.getName());

form.getChildren().add(message);

}

publicstaticValueExpressioncreateValueExpression(Field

field){

Stringel="#{dynamicForm.values['"+

field.getName()+"']}"

Page 140: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

FacesContextcontext=

FacesContext.getCurrentInstance();

ELContextelContext=context.getELContext();

return

context.getApplication().getExpressionFactory()

.createValueExpression(elContext,el,

Object.class);

}

publicMap<String,Object>getValues(){

returnvalues;

}

}

andwhereintheabstractclassFieldrepresentsyourcustommodelofaformfieldwithatleasttype,name,andlabelpropertiesandtheconcreteimplementationofaTextField#populate()lookssomethinglikethefollowing:

publicvoidpopulate(DynamicFormBeanform){

form.createOutputLabel(this);

form.createInputText(this);

form.createMessage(this);

}

NotethenamingpatternofconcreteUIComponentclasses.ForHTMLcomponentstheyfollowexactlytheconvention“Html[TagName]”.Forthe<h:inputText>that’sthusHtmlInputText,andsoon.TheaboveJavaexamplewillbasicallycreatethefollowingXMLrepresentation:

<h:outputLabelid="name_l"for="name"value="Label"/>

<h:inputTextid="name"value="#{dynamicForm.values['name']}"

/>

Page 141: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:messageid="name_m"for="name"/>

Itonlydoesthatquiteverbosely.Essentially,you’reherereinventingthejobofFacelets.There’sreallynothingwhichisimpossibleusingXMLandonlypossibleinJava.AslongasyouunderstandhowyoucanuseJSTLforthis:<h:formid="dynamicFormId">

<c:forEachitems="#{dynamicForm.fields}"

var="field">

<t:fieldtype="#{field.type}"

id="#{field.name}"label="#{field.label}"

value="#{dynamicForm.values[field.name]}">

</t:field>

</c:forEach>

</h:form>

whereinthe#{dynamicForm}insteadlookssomethinglikethefollowing:

@Named@RequestScoped

publicclassDynamicForm{

privateList<Field>fields;

privateMap<String,Object>values=newHashMap<>();

@Inject

publicFieldServicefieldService;

publicList<Field>getFields(){

if(fields=null){

FacesContextcontext=

FacesContext.getCurrentInstance();

UIComponentform=

UIComponent.getCurrentComponent(context);

fields=fieldService.list(form.getId());

Page 142: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

returnfields;

}

publicMap<String,Object>getValues(){

returnvalues;

}

}

Yousee,thereisnoneedtomesswithmanuallycreatingandpopulatingUIComponentinstances.FaceletsdoesthatallforyoubasedonsimpleXML.The<t:field>canbefoundinthesection“TagFiles”inChapter7.

Footnotes1https://javaee.github.io/javaee-

spec/javadocs/javax/faces/event/AbortProcessingException

.html.

2https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/EditableValueHolder.

html.

3https://javaee.github.io/javaee-

spec/javadocs/javax/faces/event/PhaseListener.html.

4https://javaee.github.io/javaee-

spec/javadocs/javax/faces/event/PhaseId.html.

5https://javaee.github.io/javaee-

spec/javadocs/javax/faces/lifecycle/Lifecycle.html.

6https://javaee.github.io/javaee-

spec/javadocs/javax/faces/event/ComponentSystemEvent.htm

l.

Page 143: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

7https://javaee.github.io/javaee-

spec/javadocs/javax/faces/event/ComponentSystemEventList

ener.html.

8

https://javaserverfaces.github.io/docs/2.3/vdldocs/facel

ets/c/tld-summary.html.

9https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/UIComponent.html.

Page 144: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1) (2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_4

4.FormComponents

BaukeScholtz andArjanTijms

Willemstad,Curaçao Amsterdam,Noord-Holland,TheNetherlands

ThesearethemostimportantcomponentsofthestandardJSF(JavaServerFaces)componentset.Withoutthem,JSFwouldn’thavebeenveryusefulinthefirstplace.WhenusingplainHTMLelementsinsteadofJSFcomponents,you’denduppollutingthecontrollerwithcodetomanuallyapply,convert,andvalidatesubmittedvalues;toupdatethemodelwiththosevalues;andtofigureouttheactionmethodtobeinvoked.That’sexactlythehardworkJSFshouldtakeoffyourhandsasbeingacomponent-basedMVC(Model-View-Controller)frameworkforHTMLform-basedwebapplications.

Input,Select,andCommandComponentsAllinputcomponentsextendfromtheUIInputsuperclass.Allselectioncomponentsextendfromasubclassthereof,whichcanbeUISelectBoolean,UISelectOne,orUISelectMany.(SeeChapter3,Table3-1,fora

1 2

Page 145: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

comprehensivelistofstandardJSFcomponents.)AllinputandselectcomponentsimplementtheEditableValueHolderinterfacewhichallowsattachingaConverter,Validator,andValueChangeListener.AllcommandcomponentsextendfromtheUICommandsuperclassandimplementtheActionSourceinterfacewhichallowsdefiningoneormoremanagedbeanmethodswhichshouldbeinvokedduringtheinvokeapplicationphase(fifthphase).Theycanhaveonlyone“action”methodandmultiple“actionlistener”methods.

HTMLrequiresallinput,select,andcommandelementstobenestedinaformelement.ThestandardJSFcomponentsetoffersonlyonesuchcomponent,the<h:form>,whichisfromtheUIFormsuperclass.YoucouldalsouseaplainHTML<form>element,butthatwouldn’tautomaticallyincludethemandatoryjavax.faces.ViewStatehiddeninputfieldintheformwhichrepresentstheJSFviewstate.Therendererofthe<h:form>istheoneresponsibleforautomaticallyincludingitineverygeneratedHTMLrepresentationoftheJSFform.Withoutit,JSFwon’trecognizetherequestasavalidpostbackrequest.Inotherwords,theFacesContext.getCurrentInstance().isPostba

ck()wouldreturnfalseandthenJSFwouldn’tevenprocessthesubmittedvalues,letaloneinvoketheactionmethod.AplainHTML<form>elementinaJSFpageisonlyusefulforGETrequestsincombinationwith<f:viewParam>tagswhichshouldtakecareofprocessingthesubmittedinputvalues.Thiswillbedetailedlaterinthesection“GETForms.”

Page 146: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Allcommandcomponentshaveanactionattributewhichcanbeboundtoamanagedbeanmethod.Thismethodwillbeinvokedduringtheinvokeapplicationphase(fifthphase),aslongasthere’snoconversionorvalidationerror.ConversionandvalidationarecoveredinChapter5,sowe’llskipdetailingthisstephere.

Text-BasedInputComponentsAlltext-basedinputcomponentshaveavalueattributewhichcanbeboundtoamanagedbeanproperty.Thegetterofthispropertywill,duringtheviewrendertime,beconsultedtoretrieveanddisplayanypresetvalue.And,thesetterofthispropertywill,duringtheupdatemodelvaluesphase(fourthphase)ofthepostbackrequest,beinvokedwiththesubmittedandalreadyconvertedandvalidatedvalue,ifapplicable.Followingisabasicusageexamplewhichdemonstratesalltext-basedinputcomponents.

Faceletsfile/test.xhtml:<h:form><h:inputTextvalue="#{bean.text}"/>

<h:inputSecretvalue="#{bean.password}"/>

<h:inputTextareavalue="#{bean.message}"/>

<h:inputHiddenvalue="#{bean.hidden}"/>

<h:commandButtonvalue="Submit"action="#

{bean.submit}"/>

</h:form>

Backingbeanclasscom.example.project.view.Bean:@Named@RequestScoped

publicclassBean{

Page 147: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

privateStringtext;

privateStringpassword;

privateStringmessage;

privateStringhidden;

publicvoidsubmit(){

System.out.println("Formhasbeensubmitted!");

System.out.println("text:"+text);

System.out.println("password:"+password);

System.out.println("message:"+message);

System.out.println("hidden:"+hidden);

}

//Add/generategettersandsettersforevery

propertyhere.

}

GeneratedHTMLoutput:

<formid="j_idt4"name="j_idt4"method="post"

action="projecttest.xhtml"

enctype="application/x-www-form-urlencoded">

<inputtype="hidden"name="j_idt4"value="j_idt4"/>

<inputtype="text"name="j_idt4:j_idt5"/>

<inputtype="password"name="j_idt4:j_idt6"/>

<textareaname="j_idt4:j_idt7"></textarea>

<inputtype="hidden"name="j_idt4:j_idt8"/>

<inputtype="submit"name="j_idt4:j_idt9"value="Submit"

/>

<inputtype="hidden"name="javax.faces.ViewState"

id="j_id1:javax.faces.ViewState:0"

value="-4091383829147627416:3884402765892734278"

autocomplete="off"/>

</form>

Page 148: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

RenderinginChromebrowser(withnewlinesadded):

You’llnoticeseveralthingsinthegeneratedHTMLoutput.UndoubtedlythefirstthingnoticeableisthatJSFhasalsoautomaticallygeneratedidandnameattributesoftheHTMLelements,allwithaj_idprefixwhichisdefinedbythepublicAPI(applicationprogramminginterface)constantUIViewRoot.UNIQUE_ID_PREFIX.The“t”basicallystandsfor“tree”andthenumberbasicallyrepresentsthepositionofthecomponentinthecomponenttree.Thisisthuspronetobechangedwheneveryouadd,remove,ormovearoundcomponentsintheFaceletsfile.ThisisthusalsosubjecttoheadacheswhenQA(qualityassurance)needstowriteintegrationtestsforthewebapplicationwhereinmorethanoftentheHTMLelementIDsneedtobeused.

JSFwilluseanautogeneratedIDwhenit’smandatoryforthefunctionalityinordertohaveanidand/oranameattributeinthegeneratedHTMLoutput.TheidattributeismandatoryinordertobeabletofindtheHTMLelementbyanyJavaScriptcodewhichcanalsobeautogeneratedbyJSF,suchasfunctionsresponsiblefortheAjaxworks.AsthismakesthegeneratedHTMLcoderatherhardtoreadand,

Page 149: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

frankly,ugly,we’dliketojustexplicitlyspecifytheidattributeofanyJSFform,input,select,andcommandcomponent.ThiswayJSFwilljustuseitfortheidandnameattributesoftheHTMLelementsinsteadofautogeneratingone.Now,let’srewritetheFaceletsfile/test.xhtmlforthat.AgoodpracticeistolettheIDattributeoftheinputcomponentmatchexactlythebeanpropertyname,andtheIDattributeofthecommandcomponentmatchexactlythebeanmethodname.Thiswouldendupinmoreself-documentingcodeandgeneratedHTMLoutput.

<h:formid="form">

<h:inputTextid="text"value="#{bean.text}"/>

<h:inputSecretid="password"value="#{bean.password}"/>

<h:inputTextareaid="message"value="#{bean.message}"/>

<h:inputHiddenid="hidden"value="#{bean.hidden}"/>

<h:commandButtonid="submit"value="Submit"

action="#{bean.submit}"/>

</h:form>

Now,thegeneratedHTMLoutputlooksasfollows:

<formid="form"name="form"method="post"

action="projecttest.xhtml"

enctype="application/x-www-form-urlencoded">

<inputtype="hidden"name="form"value="form"/>

<inputid="form:text"type="text"name="form:text"/>

<inputid="form:password"type="password"

name="form:password"/>

<textareaid="form:message"name="form:message">

</textarea>

<inputid="form:hidden"type="hidden"name="form:hidden"

/>

<inputid="form:submit"type="submit"name="form:submit"

value="Submit"/>

<inputtype="hidden"name="javax.faces.ViewState"

Page 150: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

id="j_id1:javax.faces.ViewState:0"

value="-7192066430460949081:-3987350607752016894"

autocomplete="off"/>

</form>

That’salreadyclearer.NotethatwhenyouexplicitlysetacomponentID,itwillalwaysendupinthegeneratedHTMLoutput.ThegeneratedHTMLelementID,then,representsthe“clientID”whichmaybedifferentfromthecomponentID,dependingonitsparents.IfthecomponenthasanyparentwhichisaninstanceofNamingContainerinterface,thentheIDoftheNamingContainerparentwillbeprependedtotheclientIDofthecomponent.FromthestandardJSFHTMLcomponentset,onlythe<h:form>and<h:dataTable>areinstancesofNamingContainer.Othersare<ui:repeat>and<f:subview>.

IfyoulookcloseratthegeneratedHTMLoutput,there’sonlyonegeneratedIDleft.It’stheoneoftheviewstatehiddeninputfield,whichisalwaysj_id1.ItrepresentstheIDoftheUIViewRootinstance,whichbydefaultcannotbesetfromaFaceletsfileon.WhenusingJSFinPortlet-basedwebapplicationsinsteadofServlet-basedwebapplications,itisoverridableandwouldrepresenttheuniquenameofthePortlet.InaPortlet-basedwebapplicationitispossibletohavemultiplePortletviewsinasingleJSFpage.Inotherwords,asingleJSFpageinaPortlet-basedwebapplicationcanhavemultipleUIViewRootinstances.

ComingbacktothegeneratedHTMLoutput,thenameattributeoftheHTMLinputelementismandatoryforHTMLinordertobeabletosendthesubmittedvaluesasrequestparametersviaHTTP.Itwillbecometherequestparameter

Page 151: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

name.Inanydecentwebbrowseryoucaninspecttherequestparametersinthe“Network”sectionofthewebdeveloper’stoolset,whichisaccessiblebypressingF12inthewebbrowser.Figure4-1showshowChromepresentsthepostbackrequestaftersubmittingtheformwithsomevaluesfilledout,asyoucanseeinthe“FormData”sectionofthefigure.

Figure4-1 ChromeDeveloperTools—Network—Headers—FormData

ThehiddeninputfieldwiththenamerepresentingtheIDofthe<h:form>willsignalJSFwhichformexactlywassubmittedduringthepostbackrequest.Thatis,asingleHTMLdocumentcanhavemultipleformelements.ThiswayJSFcan,duringtheapplyrequestvaluesphase(secondphase),determinewhetherthecurrentformcomponentwasactuallysubmitted.ItwillcausetheUIForm#isSubmitted()oftheformcomponenttoreturntrue.Thehiddeninputfieldwith

Page 152: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

thenamejavax.faces.ViewStaterepresentstheuniqueidentifierreferringtheserializedviewstateobjectwhichisstoredinthesession.BothhiddeninputfieldsareautomaticallyincludedbytherendererassociatedwiththeUIFormcomponent.Theautocomplete="off"ontheviewstatehiddeninputfieldis,bytheway,notatechnicalrequirementbutjustawork-aroundagainstsomebrowsersoverridingitwiththelastknownvaluewhenthebackbuttonispressed,whichmaynotbethecorrectvalueperse.

Ourexamplehiddeninputfieldhasanemptyvalue.It’seffectivelyuselessinthisform.SuchahiddeninputfieldisgenerallyonlyusefulwhenitsvalueisbeingsetbysomeJavaScriptcodewhichyou’dliketocaptureinthemanagedbean.There’sgenerallynopointto“transferring”managedbeanpropertiesfromonetothenextrequestusinghiddeninputfields.Instead,suchpropertiesshouldbeassignedtoamanagedbeanwhichisdeclaredtobeinabroaderscopethantherequestscope,suchastheview,flow,orsessionscope.Thissavestheeffortofhasslingwithhiddeninputfields.ThebeanscopeswillbedetailedinChapter8.

Theotherrequestparametersshouldspeakforthemselvesifyou’refamiliarwithbasicHTML.Theyrepresentthename/valuepairsoftheinvolvedinputelements.Youshouldbeabletodeterminewhichvalueswereactuallyenteredintheformpriortosubmitting.JSFwillalsobeabletodothesame.Itwilltraversethecomponenttreeandusethe“clientID”ofthecomponentasrequestparameternametoobtainthevaluefromtherequestparametermap.Basically,thefollowingcodewillunderthehoodbeexecutedforeachinputcomponentduringapplyrequestvaluesphase(secondphase).This

Page 153: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

happensintheUIInput#decode()method.

FacesContextcontext=FacesContext.getCurrentInstance();

ExternalContextexternalContext=

context.getExternalContext();

Map<String,String>formData=

externalContext.getRequestParameterMap();

StringclientId=component.getClientId(context);

StringsubmittedValue=formData.get(clientId);

component.setSubmittedValue(submittedValue);

And,duringthesamephase,thefollowingcodeisbasicallyexecutedforeachcommandcomponentinthedecodemethodoftherendererassociatedwiththecomponent:if(formData.get(clientId)!=null){

component.queueEvent(newActionEvent(context,

component));

}

Duringtheprocessvalidationsphase(thirdphase),JSFwillsetthesubmittedvalueofeveryinvolvedinputcomponentas“localvalue”afterperformingthenecessaryconversionandvalidationifanyconverterorvalidatorisregisteredonthecomponentorassociatedbeanpropertyandhasexecutedwithouterrors.ThishappensintheUIInput#validate()methodwhosecorelogicisshowninthefollowingcodeina(very!)simplifiedform:

StringsubmittedValue=component.getSubmittedValue();

try{

Converterconverter=component.getConverter();

ObjectnewValue=

component.getConvertedValue(submittedValue);

Page 154: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

for(Validatorvalidator:component.getValidators()){

validator.validate(context,component,newValue);

}

component.setValue(newValue);

component.setSubmittedValue(null);

}

catch(ConverterException|ValidatorExceptione){

context.addMessage(clientId,e.getFacesMessage());

context.validationFailed();//Skipsphases4and5.

component.setValid(false);

}

WhentherearenovalidationerrorsandtheFacesContext#isValidationFailed()thusreturnsfalse,thenJSFwilladvancetotheupdatemodelvaluesphase(fourthphase).Duringthisphase,the“localvalue”oftheinputcomponentswillultimatelybesetasmanagedbeanpropertiesassociatedwiththevalueattributeoftheinputcomponents.ThiswillhappenintheUIInput#updateModel()methodwhichissimplifiedasfollows:

ValueExpressionel=component.getValueExpression("value");

if(el!=null){

el.setValue(context.getELContext(),

component.getValue());

component.setValue(null);

}

TheelvariablebasicallyrepresentstheExpressionLanguage(EL)statementasdefinedinthevalueattribute,whichis,incaseofour<h:inputText>example,thus#{bean.text}.TheValueExpression#setValue()willbasicallytriggerthesettermethodbehindthisexpression

Page 155: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

withthecomponent’svalue.So,effectivelyitwillexecutebean.setText(component.getValue()).

Onceallmodelvalueshavebeenupdated,JSFwilladvancetotheinvokeapplicationphase(fifthphase).AnyActionEventwhichis,duringtheapplyrequestvaluesphase(secondphase),queuedinacommandcomponentwillbebroadcasted.Itwillultimatelyinvokeallmethodsassociatedwiththecommandcomponent.Inthecaseofour<h:commandButton>example,whichhas#{bean.submit}definedasanactionattribute,itwillinvoketheBean#submit()method.Finally,JSFwilladvancetothelastphase,therenderresponsephase(sixthphase),generatingtheHTMLoutputandtherebyinvokingthegettermethodsinordertoobtainthemodelvaluestobeembeddedintheHTMLoutput.

File-BasedInputComponentYes,thereisonlyonefile-basedinputcomponent.That’sthe<h:inputFile>.Ithasonlyoneadditionalrequirementonthe<h:form>itisbeingplacedin,itsenctypeattributehastobeexplicitlysettomultipart/form-datatoconformtheHTMLspecification.Thishasnoeffectonotherinputcomponents;theywillcontinuetoworkjustfine.It’sjustthatthedefaultformencodingapplication/x-www-form-urlencodeddoesn’tsupportembeddingbinarydata.Themultipart/form-dataencodingsupportsthis,butitisonlyslightlymoreverbose.Everyrequestparametervalueisprecededbyaboundaryline,aContent-Dispositionheaderwiththerequestparametername,aContentTypeheaderwith

Page 156: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

thecontenttypeofthevalue,andtwonewlines.ItisveryinefficientcomparedtothedefaultencodingwhereintheURLencodedrequestparametername/valuepairsarejustconcatenatedbythe&character,butit’sactuallytheonlyreliablewaytobeabletoembedfilesinaHTTPPOSTrequestwithoutinducingambiguity,particularlywhenuploadingtextfileswhosecontentcoincidentallyresemblesname/valuepairs.

Thevalueattributeofthe<h:inputFile>shouldbeboundtoabeanpropertyofthejavax.servlet.http.Partinterface.

Faceletsfile/test.xhtml:<h:formid="form"enctype="multipart/form-data">

<h:inputFileid="file"value="#{bean.file}"/>

<h:commandButtonid="submit"value="Submit"

action="#{bean.submit}"/>

</h:form>

Backingbeanclasscom.example.project.view.Bean:@Named@RequestScoped

publicclassBean{

privatePartfile;

publicvoidsubmit()throwsIOException{

System.out.println("Formhasbeensubmitted!");

System.out.println("file:"+file);

if(file!=null){

System.out.println("name:"+

file.getSubmittedFileName());

Page 157: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

System.out.println("type:"+

file.getContentType());

System.out.println("size:"+

file.getSize());

InputStreamcontent=file.getInputStream();

//WritecontenttodiskorDB.

}

}

//Add/generategettersandsettersforevery

propertyhere.

}

GeneratedHTMLoutput:

<formid="form"name="form"method="post"

action="projecttest.xhtml"

enctype="multipart/form-data">

<inputtype="hidden"name="form"value="form"/>

<inputid="form:file"type="file"name="form:file"/>

<inputid="form:submit"type="submit"name="form:submit"

value="Submit"/>

<inputtype="hidden"name="javax.faces.ViewState"

id="j_id1:javax.faces.ViewState:0"

value="6034213708100805615:8835868421785849982"

autocomplete="off"/>

</form>

RenderinginChromebrowser(withnewlinesadded):

Therequestprocessinglifecycleisthesameasfortext-based

Page 158: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

inputcomponents,exceptfortheapplyrequestvaluesphase(secondphase).InsteadofextractingthesubmittedfileasarequestparameterintheUIInput#decode()method,thesubmittedfileisbeingextractedasarequestpartintherendererassociatedwiththefileinputcomponent.Thedefaultimplementationbasicallylooksasfollows:FacesContextcontext=FacesContext.getCurrentInstance();

ExternalContextec=context.getExternalContext();

HttpServletRequestrequest=(HttpServletRequest)

ec.getRequest();

StringclientId=component.getClientId(context);

PartsubmittedValue=request.getPart(clientId);

component.setSubmittedValue(submittedValue);

SelectionComponentsJSFoffersabunchofselectioncomponentsoftheUISelectBoolean,UISelectOne,andUISelectManycomponentfamilieswhichallextendfromUIInput.ExceptfortheUISelectBoolean,theyallexpecttheavailableitemsforselectiontobeprovidedvia<f:selectItems>or<f:selectItem>tagsnestedintheselectioncomponent.ThevalueattributeofaUISelectBooleancomponentcanonlybeboundtoabeanpropertyofbooleanorBooleantypeanddoesn’tsupportaconverter,whileotherssupportaconverter.ThevalueattributeofaUISelectOnecomponenthastobeboundtoasingle-valuepropertysuchasString,andthevalueattributeofaUISelectManycomponentcanonlybeboundtoa

Page 159: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

multi-valuepropertysuchasCollection<String>orString[].

Inreal-worldHTML-basedwebapplications,the<h:selectOneListbox>(single-selectlistbox)and<h:selectManyMenu>(multi-selectdrop-down)aren’tterriblyuseful.Generallythe<h:selectOneMenu>(single-selectdrop-down)and<h:selectManyListBox>(multi-selectlistbox)arepreferredastheyaremoreuserfriendly.Followingisabasicusageexamplewhichdemonstratesallselectioncomponentsexceptfortheaforementionedleastusefulones.Incaseyouwanttousethemanyway,justfollowthedemonstratedapproachwithadifferenttagname.

Faceletsfile/test.xhtml:<h:formid="form"><h:selectBooleanCheckboxid="checked"value="#

{bean.checked}"/>

<h:selectOneMenuid="oneMenu"value="#

{bean.oneMenu}">

<f:selectItemsvalue="#{bean.availableItems}"/>

</h:selectOneMenu>

<h:selectOneRadioid="oneRadio"value="#

{bean.oneRadio}">

<f:selectItemsvalue="#{bean.availableItems}"/>

</h:selectOneRadio>

<h:selectManyListboxid="manyListbox"value="#

{bean.manyListbox}">

<f:selectItemsvalue="#{bean.availableItems}"/>

</h:selectManyListbox>

<h:selectManyCheckboxid="manyCheckbox"value="#

{bean.manyCheckbox}">

Page 160: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:selectItemsvalue="#{bean.availableItems}"/>

</h:selectManyCheckbox>

<h:commandButtonid="submit"value="Submit"

action="#{bean.submit}"/>

</h:form>

Backingbeanclasscom.example.project.view.Bean:@Named@RequestScoped

publicclassBean{

privatebooleanchecked;

privateStringoneMenu;

privateStringoneRadio;

privateList<String>manyListbox;

privateList<String>manyCheckbox;

privateList<String>availableItems;

@PostConstruct

publicvoidinit(){

availableItems=Arrays.asList("one","two",

"three");

}

publicvoidsubmit(){

System.out.println("Formhasbeensubmitted!");

System.out.println("checked:"+checked);

System.out.println("oneMenu:"+oneMenu);

System.out.println("oneRadio:"+oneRadio);

System.out.println("manyListbox:"+

manyListbox);

Page 161: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

System.out.println("manyCheckbox:"+

manyCheckbox);

}

//Add/generategettersandsettersforevery

propertyhere.

//NotethatavailableItemspropertydoesn’tneeda

setter.

}

GeneratedHTMLoutput:

<formid="form"name="form"method="post"

action="projecttest.xhtml"

enctype="application/x-www-form-urlencoded">

<inputtype="hidden"name="form"value="form"/>

<inputid="form:checked"type="checkbox"

name="form:checked"/>

<selectid="form:oneMenu"name="form:oneMenu"size="1">

<optionvalue="one">one</option>

<optionvalue="two">two</option>

<optionvalue="three">three</option>

</select>

<tableid="form:oneRadio">

<tr>

<td>

<inputid="form:oneRadio:0"type="radio"

name="form:oneRadio"value="one"/>

<labelfor="form:oneRadio:0">one</label>

</td>

<td>

<inputid="form:oneRadio:1"type="radio"

name="form:oneRadio"value="two"/>

<labelfor="form:oneRadio:1">two</label>

</td>

<td>

<inputid="form:oneRadio:2"type="radio"

Page 162: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

name="form:oneRadio"value="three"/>

<labelfor="form:oneRadio:2">three</label>

</td>

</tr>

</table>

<selectid="form:manyListbox"name="form:manyListbox"

multiple="multiple"size="3">

<optionvalue="one">one</option>

<optionvalue="two">two</option>

<optionvalue="three">three</option>

</select>

<tableid="form:manyCheckbox">

<tr>

<td>

<inputid="form:manyCheckbox:0"

type="checkbox"

name="form:manyCheckbox"value="one"/>

<labelfor="form:manyCheckbox:0">one</label>

</td>

<td>

<inputid="form:manyCheckbox:1"

type="checkbox"

name="form:manyCheckbox"value="two"/>

<labelfor="form:manyCheckbox:1">two</label>

</td>

<td>

<inputid="form:manyCheckbox:2"

type="checkbox"

name="form:manyCheckbox"value="three"/>

<labelfor="form:manyCheckbox:2">

three</label>

</td>

</tr>

</table>

<inputid="form:submit"type="submit"name="form:submit"

value="Submit"/>

<inputtype="hidden"name="javax.faces.ViewState"

id="j_id1:javax.faces.ViewState:0"

value="403461711995663039:117935361680169981"

autocomplete="off"/>

Page 163: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</form>

RenderinginChromebrowser(withnewlinesadded):

InthegeneratedHTMLoutput,you’llimmediatelynoticethat<h:selectOneRadio>and<h:selectManyCheckbox>generateanHTMLtablearoundtheinputs.SuchamarkupisindeedfrowneduponsinceWeb2.0.ThisissomewhataleftoverofJSF1.0,whenWeb2.0didn’texistyet.Forthe<h:selectManyCheckbox>thiscouldeasilybeworkedaroundbyusingabunchof<h:selectBooleanCheckbox>componentsinthedesiredHTMLmarkupwhichareboundagainstaslightlyadjustedmodel.

Faceletsfile/test.xhtml:<h:formid="form"><ul>

<ui:repeatid="many"value="#

{bean.availableItems}"var="item">

Page 164: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<li>

<h:selectBooleanCheckboxid="checkbox"

value="#

{bean.manyCheckboxMap[item]}"/>

<h:outputLabelfor="checkbox"value="#

{item}"/>

</li>

</ui:repeat>

</ul>

<h:commandButtonid="submit"value="Submit"

actionListener="#{bean.collectCheckedValues}"

action="#{bean.submit}"/>

</h:form>

Backingbeanclasscom.example.project.view.Bean:@Named@RequestScoped

publicclassBean{

privateList<String>manyCheckbox;

privateList<String>availableItems;

privateMap<String,Boolean>manyCheckboxMap=new

LinkedHashMap<>();

@PostConstruct

publicvoidinit(){

availableItems=Arrays.asList("one","two",

"three");

}

publicvoidcollectCheckedValues(){

Page 165: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

manyCheckbox=

manyCheckboxMap.entrySet().stream()

.filter(e->e.getValue())

.map(Map.Entry::getKey)

.collect(Collectors.toList());

}

publicvoidsubmit(){

System.out.println("Formhasbeensubmitted!");

System.out.println("manyCheckbox:"+

manyCheckbox);

}

//Add/generategettersforavailableItemsand

manyCheckboxMap.

//Notethatsettersarenotnecessaryforthem.

}

GeneratedHTMLoutput:

<formid="form"name="form"method="post"

action="projecttest.xhtml"

enctype="application/x-www-form-urlencoded">

<inputtype="hidden"name="form"value="form"/>

<ul>

<li>

<inputid="form:many:0:checkbox"type="checkbox"

name="form:many:0:checkbox"/>

<labelfor="form:many:0:checkbox">one</label>

</li>

<li>

<inputid="form:many:1:checkbox"type="checkbox"

name="form:many:1:checkbox"/>

<labelfor="form:many:1:checkbox">two</label>

</li>

Page 166: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<li>

<inputid="form:many:2:checkbox"type="checkbox"

name="form:many:2:checkbox"/>

<labelfor="form:many:2:checkbox">three</label>

</li>

</ul>

<inputid="form:submit"type="submit"name="form:submit"

value="Submit"/>

<inputtype="hidden"name="javax.faces.ViewState"

id="j_id1:javax.faces.ViewState:0"

value="-2278907496447873737:-4769857814543424434"

autocomplete="off"/>

</form>

RenderinginChromebrowser:

That’salreadymoreWeb2.0friendly.Thebulletsofthe<ul>canofcoursebehiddenbysettingtheCSS(CascadingStyleSheets)list-style-typepropertytonone.NotethattheactionListenerattributeofthe<h:commandButton>alwaysrunsbeforetheactionattribute.Thesameapproachwasnotpossiblefor<h:selectOneRadio>foralongtime.There’snosuchcomponentas<h:radioButton>oranythinglikethat.Solutionsweresoughtinthird-partycomponentlibrariessuchasPrimeFaces.SinceJSF2.2thiscouldbetrickedwiththenew“pass-throughelements”and“pass-throughattributes”featureonplainHTML<inputtype="radio">elements. OnlysinceJSF2.3hasitbeennativelypossible1

Page 167: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

withhelpofthenewgroupattributewhichbasicallyrepresentsthesameasthenameattributeoftheplainHTML<inputtype="radio">element.

Faceletsfile/test.xhtml:<h:formid="form"><ul>

<ui:repeatid="one"value="#

{bean.availableItems}"var="item">

<li>

<h:selectOneRadioid="radio"

group="groupName"

value="#{bean.oneRadio}">

<f:selectItemitemValue="#{item}"/>

</h:selectOneRadio>

<h:outputLabelfor="radio"value="#

{item}"/>

</li>

</ui:repeat>

</ul>

<h:commandButtonid="submit"value="Submit"

action="#{bean.submit}"/>

</h:form>

Backingbeanclasscom.example.project.view.Bean:@Named@RequestScoped

publicclassBean{

privateStringoneRadio;

privateList<String>availableItems;

@PostConstruct

Page 168: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicvoidinit(){

availableItems=Arrays.asList("one","two",

"three");

}

publicvoidsubmit(){

System.out.println("Formhasbeensubmitted!");

System.out.println("oneRadio:"+oneRadio);

}

//Add/generategettersandsettersforevery

propertyhere.

//NotethatavailableItemspropertydoesn’tneeda

setter.

}

GeneratedHTMLoutput:

<formid="form"name="form"method="post"

action="projecttest.xhtml"

enctype="application/x-www-form-urlencoded">

<inputtype="hidden"name="form"value="form"/>

<ul>

<li>

<inputtype="radio"id="form:one:0:radio"

name="form:groupName"

value="form:one:0:radio:one"/>

<labelfor="form:one:0:radio">one</label>

</li>

<li>

<inputtype="radio"id="form:one:1:radio"

name="form:groupName"

value="form:one:1:radio:two"/>

<labelfor="form:one:1:radio">two</label>

</li>

Page 169: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<li>

<inputtype="radio"id="form:one:2:radio"

name="form:groupName"

value="form:one:2:radio:three"/>

<labelfor="form:one:2:radio">three</label>

</li>

</ul>

<inputid="form:submit"type="submit"name="form:submit"

value="Submit"/>

<inputtype="hidden"name="javax.faces.ViewState"

id="j_id1:javax.faces.ViewState:0"

value="3336433674711048358:164229014603307903"

autocomplete="off"/>

</form>

RenderinginChromebrowser:

Technically,the<h:selectManyCheckbox>couldsupportthegroupattributetoo,butthishasn’tyetbeenimplemented.PerhapsitwillbeinJSF.next.

SelectItemTagsProvidingavailableitemsforUISelectOneandUISelectManycomponentscanbedoneinseveralways.Asdemonstratedintheprevioussection,youcanusethe<f:selectItems>and<f:selectItem>tagsnestedintheselectioncomponentforthis.Youcanusethe<f:selectItem>tagtodefinetheavailableitemsentirelyontheviewside.Followingisanexampleusing

Page 170: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:selectOneMenu>,butyoucanuseitthesamewayinanyotherUISelectOneandUISelectManycomponent:<h:selectOneMenuid="selectedItem"value="#

{bean.selectedItem}">

<f:selectItemitemValue="#{null}"itemLabel="--

selectone--"/>

<f:selectItemitemValue="one"itemLabel="Firstitem"

/>

<f:selectItemitemValue="two"itemLabel="Second

item"/>

<f:selectItemitemValue="three"itemLabel="Third

item"/>

</h:selectOneMenu>

Notethataselectitemwithvalueof#{null}canbeusedtopresentthedefaultselectionincasethebeanpropertyassociatedwithselectioncomponent’svalueattributeisnull.Ifyouhaveconsultedthetagdocumentationof<f:selectItem>,thenyou’llperhapshavenoticedthenoSelectionOptionattributeandhavethoughtthatitwasintendedtorepresenta“noselectionoption.”Actually,thisisn’ttrue.Manystartersindeedthinkso,asyoucanseeinmanyforums,Q&Asites,andpoor-qualitytutorialsontheInternet.Inspiteofthemisleadingattributename,itdoesnotrepresenta“noselectionoption.”AbetterattributenamewouldhavebeenhideWhenOtherOptionIsSelected,andeventhenitworksonlywhentheparentselectioncomponenthasexplicitlyahideNoSelectionOption="true"attributesetliketheonethatfollows:<h:selectOneMenuid="selectedItem"value="#{bean.selectedItem}"

Page 171: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

hideNoSelectionOption="true">

<f:selectItemitemValue="#{null}"itemLabel="--

selectone--"

noSelectionOption="true"/>

<f:selectItemitemValue="one"itemLabel="Firstitem"

/>

<f:selectItemitemValue="two"itemLabel="Second

item"/>

<f:selectItemitemValue="three"itemLabel="Third

item"/>

</h:selectOneMenu>

So,hideWhenOtherOptionIsSelectedAndHideNoSele

ctionOptionIsTruewouldultimatelyhavebeenthemostself-explanatoryattributename.Unfortunately,thiswasn’tverywellthoughtoutwhenthenoSelectionOptionwasimplementedinJSF1.2.Requiringtwoattributesforthisattributetofunctionshouldn’thavebeennecessary.Theprimarypurposeofthisattributepairistopreventthewebsiteuserfrombeingabletoreselectthe“noselectionoption”whenthecomponenthasalreadyanon-nullvalue—forexample,byhavingitpreparedina@PostConstructmethod,orbyre-renderingthecomponentafteraformsubmitwithanon-nullvalue.

Thatsaid,theitemValueattributeofthe<f:selectItem>representsthevaluethatwillbesetasbeanpropertywhentheformissubmitted,andthevaluethatwillbepreselectedfromanynon-nullbeanpropertywhentheHTMLoutputistobegenerated.TheitemLabelattributerepresentsthelabelthatwillbedisplayedtotheweb

Page 172: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

siteuser.WhentheitemLabelattributeisabsent,JSFwilldefaulttoitemValue.Notethatthelabelisinnowaysubmittedbacktotheserver.Thatis,inthegeneratedHTMLoutput,the<option>labelisnotpartofthe<option>value.

Youcanusethe<f:selectItems>tagtoreferenceaCollection,Map,orarrayofavailableitemsinthebackingbean.Youcanevenmixthiswith<f:selectItem>tags.

<h:selectOneMenuid="selectedItem"value="#

{bean.selectedItem}">

<f:selectItemitemValue="#{null}"itemLabel="--select

one--"/>

<f:selectItemsvalue="#{bean.availableItems}"/>

</h:selectOneMenu>

Theywillberenderedinthesameorderastheyaredeclaredintheview.OnlywhenyouuseanunorderedMapimplementationasvalue,suchasHashMap,theorderofitemsprovidedby<f:selectItems>willbeundefined.It’sthereforebettertouseanorderedMapimplementation,suchasTreeMaporLinkedHashMap.WhenpopulatingtheavailableitemsasaMap,keepinmindthatthemapkeyrepresentstheitemlabelandthemapvaluerepresentstheitemvalue.You’dperhapsintuitivelyexpectittobetheotherwayaround,butthiswasatechnicallimitation.Thatis,ontheJavaside,themapkeyenforcesuniquenesswhilethemapvaluedoesn’t.AndontheHTMLside,theoptionlabelissupposedtobeuniquewhiletheoptionvaluedoesn’tneedtobe.Followingishowyoucanpopulatesuchamap:privateMap<String,String>availableItems;

Page 173: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@PostConstruct

publicvoidinit(){

availableItems=newLinkedHashMap<>();

availableItems.put("Firstitem","one");

availableItems.put("Seconditem","two");

availableItems.put("Thirditem","three");

}

//Add/generategetter.Notethatasetteris

unnecessary.

Assaid,youcanalsouseaTreeMaporHashMap,butthentheitemlabelswillbecome,respectively,sortedorunsorted,regardlessoftheinsertionorder.

Incaseyou’dreallyliketoswapthemapkeysandvaluesaroundontheviewside,youcanalwaysdosobymanuallyassigningthemapentryvalueasanitemlabelandthemapentrykeyasanitemvalue.Youcandothatwithhelpofthevarattributeofthe<f:selectItems>bywhichyoucandeclaretheELvariablenameofthecurrentlyiterateditem.Thiscan,inturn,beaccessedintheitemValueanditemLabelattributesofthesametag.WhenyoupassMap#entrySet()tothevalueattributeofthe<f:selectItems>,theneachiterateditemwillrepresentaMap.Entryinstance.Thishas,inturn,getKey()andgetValue()methodswhicharethusperfectlyusableasELproperties.

<f:selectItemsvalue="#{bean.availableItems.entrySet()}"

var="entry"

Page 174: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

itemValue="#{entry.key}"itemLabel="#{entry.value}">

</f:selectItems>

ThisalsoworkswhenusingaCollectionorarrayasavailableitems.Youdon’texplicitlyneedtofirstconvertittoaSet(morespecifically,Iterable),asdemonstratedabove.ThisisparticularlyusefulwhenyouhaveaCollectionoranarrayofcomplexobjectsasavailableitems,suchasmodelentities.

Modelentityrepresentinga“country”:

publicclassCountry{

privateLongid;

privateStringcode;

privateStringname;

//Add/generategettersandsetters.

}

Backingbean:

@Named@RequestScoped

publicclassBean{

privateStringcountryCode;

privateList<Country>availableCountries;

@Inject

privateCountryServicecountryService;

@PostConstruct

publicvoidinit(){

availableCountries=countryService.getAll();

}

Page 175: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

//Add/generategettersandsetters.

//Notethatasetterisunnecessaryfor

availableCountries.

}

View:

<h:selectOneMenuid="countryCode"value="#

{bean.countryCode}">

<f:selectItemitemValue="#{null}"itemLabel="--select

one--"/>

<f:selectItemsvalue="#{bean.availableCountries}"

var="country">

itemValue="#{country.code}"itemLabel="#

{country.name}"

</f:selectItems>

</h:selectOneMenu>

Notethatanypersistenceframework-specificannotations,suchasJPA’s@Entityand@Id,andtheactualimplementationofCountryService,areomittedforclarity.Thoseareirrelevanttoanyfront-endframework,suchasJSF.

Withtheaboveconstruct,thevalueasobtainedfromCountry#getCode()willendupasvalueofthegeneratedHTML<option>element.Now,whentheformissubmitted,itwillbecomethesubmittedvalueoftheselectioncomponent,whichwillinturninvokethesettermethodbehindthe#{bean.countryCode}propertywithexactlythatvalue.Ofcourse,youcanalsousethewholeCountryobjectasthepropertyvalueoftheselectioncomponent,butthatwouldrequireaconverterwhichcanconvertbetweenthecomplexobjectandauniquestringsuitabletobeembeddedinHTMLoutputandsentasanHTTPrequestparameter.You

Page 176: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

canreadmoreinChapter5.

SelectItemGroupIncaseyou’dliketogroupabunchofoptionsunderacommonlabel,youcanusethejavax.faces.model.SelectItemGroup,whichyouinturnreferenceinthevalueattributeofthe<f:selectItems>.Unfortunately,thiscannotbedonedeclarativelyintheFaceletsfileonacustomnestedmodel.YoureallyhavetomapyourmodelintotheJSF-providedjavax.faces.model.SelectItemforthis.Followingisakickoffexample:privateList<SelectItem>availableItems;

@PostConstruct

publicvoidinit(){

SelectItemGroupgroup1=newSelectItemGroup("Group

1");

group1.setSelectItems(newSelectItem[]{

newSelectItem("Group1Value1","Group1Label

1"),

newSelectItem("Group1Value2","Group1Label

2"),

newSelectItem("Group1Value3","Group1Label

3")

});

SelectItemGroupgroup2=newSelectItemGroup("Group

2");

group2.setSelectItems(newSelectItem[]{

newSelectItem("Group2Value1","Group2Label

Page 177: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

1"),

newSelectItem("Group2Value2","Group2Label

2"),

newSelectItem("Group2Value3","Group2Label

3")

});

availableItems=Arrays.asList(group1,group2);

//Add/generategetterforavailableItems.

//Notethatasetterisunnecessary.

}

NotedthatbothmodelAPIshavebasicallynotchangedsinceJSF1.0(2004)andthat’swhyyoustillseeaSelectItemGroup#setSelectItems()methodtakingaSelectItem[]arrayinsteadofaSelectItem…varargsargument.ThiswillcertainlybeworkedonforJSF.next.Whenreferencingitas<f:selectItemsvalue="#{bean.availableItems}"/>inanyselectioncomponent,belowishowitwouldlookforeachofthem.

The<h:selectOneMenu>willrendereachgroupas

HTML<optgroup>:

The<h:selectOneRadiolayout="pageDirection">willrenderitasanested

Page 178: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

The<h:selectManyCheckboxlayout="pageDirection">willrenderitasanestedtable:

Notetheimportanceofthelayout="pageDirection"attributein<h:selectOneRadio>and<h:selectManyCheckbox>.Thiswilllookmuchbetterthanthedefaultoflayout="lineDirection"whichwouldrendereverythinginabigsingletablerow.

LabelandMessageComponents

table:

The<h:selectManyListbox>willrendereachgroupas

HTML<optgroup>:

Page 179: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Inanaveragewell-designedform,inputelementsareusuallyaccompaniedwithalabelelementandamessageelementtargetingtheinputfield.InHTML,labelsarerepresentedbythe<label>element.InJSF,youcanusethe<h:outputLabel>componenttogenerateanHTML<label>element.HTMLdoesnothaveadedicatedelementtorepresentamessage.InJSF,the<h:message>componentgeneratesaHTML<span>elementandthe<h:messages>componentgenerateseithera<ul>elementora<table>element,dependingonthevalueofthelayoutattribute.

ThelabelelementhasvariousSEO(searchengineoptimization)andusabilityadvantages.Ittellsintextabouttheassociatedinputelement.Screenreaders,likethoseusedbypeoplewithvisualdisabilities,willfindthelabelandtellitscontentsbysound.Searchbotswillfindthelabelandindextheassociatedinputelementassuch.And,thelabelwillfocusandactivatetheassociatedinputelementwhenbeingclickeditself.Text-basedinputelementswillthenshowthetextcursor.Checkboxandradioinputelementswillthenbetoggled.Listboxanddrop-downinputelementswillthenbefocused.Fileinputelementswillthenopenthebrowsedialog.Submitbuttonswillthenbeinvoked.

Themessageelementisusuallytobeusedtodisplayconversionandvalidationerrormessagescomingfromtheserverside.Thisway,theenduserisinformedaboutthestateoftheformandcanactaccordingly,usuallybycorrectingtheinputvalues.Youcanalsouseittodisplaywarningorinformalmessages.

InJSF,the<h:outputLabel>,<h:message>and<h:messages>componentshaveaforattributewhereinyounormallydefinetheIDoftheassociatedUIInputcomponent.Followingisanexampleintheflavorofaloginform:<h:formid="login"><fieldset>

<legend>Login</legend>

<section>

<h:outputLabelfor="email"value="Emailaddress"/>

Page 180: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:inputTextid="email"value="#{login.email}"

required="true"/>

<h:messageid="m_email"for="email"/>

</section>

<section>

<h:outputLabelfor="password"value="Password"/>

<h:inputSecretid="password"value="#{login.password}"

required="true"/>

<h:messageid="m_password"for="password"/>

</section>

<footer>

<h:commandButtonid="submit"value="Login"

action="#{login.submit}"/>

</footer>

</fieldset>

</h:form>

Youcanactuallyuseanyarbitrarycomponentsearchexpressionintheforattribute.Forthe<h:outputLabel>component,thisdoesn’tmakemuchsense.Forthe<h:message>and<h:messages>components,referringtheIDofanon-UIInputcomponentwouldonlymakesensewhenyouwanttoprogrammaticallyaddafacesmessagefromthemanagedbeanon.ButyouwouldthenneedtoknowtheclientIDofthetargetcomponent.

<h:formid="login">

...

<h:commandButtonid="submit"value="Login"

action="#{login.submit}"/>

<h:messageid="m_submit"for="submit"/>

...

</h:form>

Page 181: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Theabove<h:commandButton>willgenerateaclientIDof"login:submit"intheHTMLoutput.Youcanthenprogrammaticallyaddafacesmessageasfollows:publicStringsubmit(){try{

yourAuthenticator.authenticate(email,password);

return"userhome.xhtml?faces-redirect=true";

}

catch(YourAuthenticationExceptione){

FacesContextcontext=FacesContext.getCurrentInstance();

FacesMessagemessage=newFacesMessage("Authentication

failed");

context.addMessage("login:submit",message);

returnnull;

}

}

Abetterpractice,however,istoaddthefacesmessageasaglobalmessagebypassingnullastheclientID.

context.addMessage(null,message);

Suchamessagewillthenonlyendupina<h:messagesglobalOnly="true">.

<h:commandButtonid="submit"value="Login"

action="#{login.submit}"/>

<h:messagesid="messages"globalOnly="true"

rendered="#{component.namingContainer.submitted}"/>

Notethelogicintherenderedattributeofthemessagescomponent.ItwillthusonlyberenderedwhenthesubmittedpropertyoftheNamingContainerparentevaluatestotrue.Inthisspecificcase,it’s

Page 182: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

consultingtheUIForm#isSubmitted().Thisisveryusefulincaseyouhavemultiplenon-Ajaxformseachwithitsownglobalmessagescomponentand/ora“catch-all”<h:messagesredisplay="false">componentsomewherenearthebottomoftheJSFpage,whichisthenusingCSSfixedpositionedontop.Otherwisetheglobalmessagewouldunintentionallyshowupoverthereaswell.

Thismessage-renderinglogicisnotnecessaryinAjaxformsasyoucouldjustfine-tunethemessagerenderingbysimplyexplicitlyspecifyingthemessage(s)component(s)intherenderattributeof<f:ajax>.Moreover,theUIForm#isSubmitted()wouldunexpectedlyreturnfalsewhentheexecuteattributeofthe<f:ajax>doesnotexplicitlytargettheformasinexecute="@form".

CommandComponentsYou’llhavenoticedexamplesofthe<h:commandButton>intheprevioussectionsaboutinputandselectcomponents.ThisthusgeneratesanHTML<inputtype="submit">element,whichistheHTMLwaytosendallinputvaluesofthe<form>elementitissittingintotheserver.Ontheserverside,thiscomponentisalsocapableofinvokingoneormoreJavamethods,whichareusuallydefinedintheactionoractionListenerattribute,orviaa<f:actionListener>tagnestedinthecommandcomponent.You’llalsohavereadthattheactionlistenermethodalwaysrunsbeforethemethodassociatedwiththeactionattribute.

Youcanusethe<f:actionListener>tagtoregisteroneormoreadditionalactionlistenersontheverysamecommandcomponent.Allthoseactionlistenersareinvokedinthesameorderasthey’redeclaredintheviewandattachedtothecomponent.Thetargetmethodcanbedeclaredinthreewaysonthe<f:actionListener>tag.Onewayisviathetypeattributeandtheothertwowaysareviathebindingattribute.

Page 183: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:commandButton...>

<f:actionListenertype="com.example.project.SomeActionListener"/>

<f:actionListenerbinding="#{beanImplementingActionListener}"/>

<f:actionListenerbinding="#{bean.someActionListenerMethod()}"/>

<h:commandButton>

ThetypeattributeinthefirstwaymustbasicallyrepresentthefullyqualifiednameoftheclassimplementingtheActionListenerinterface.

packagecom.example.project;

importjavax.faces.event.ActionListener;

importjavax.faces.event.ActionEvent;

publicclassSomeActionListenerimplementsActionListener{

@Override

publicvoidprocessAction(ActionEventevent){

//...

}

}

ThebindingattributeinthesecondwaymustbasicallyreferenceamanagedbeaninstanceimplementingtheActionListenerinterface.

@Named@RequestScoped

publicclassBeanImplementingActionListenerimplementsActionListener{

@Override

publicvoidprocessAction(ActionEventevent){

//...

}

}

Andthebindingattributeinthethirdwaycanbasicallyreferenceanyarbitrarymanagedbeanmethodwhichisdeclaredvoid.

Page 184: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Named@RequestScoped

publicclassBean{

publicvoidsomeActionListenerMethod(){

//...

}

}

Notethatthethirdwayismoreorlessundocumented.IthasonlybeenpossiblesincetheintroductionofEL2.2(2009),whereindeveloperscouldstartexplicitlydeclaringmethodexpressionsbysimplyaddingtheparenthesis,ifnecessarywitharguments.Coincidentally,thebindingattributeofthe<f:actionListener>coulddealwiththem.UnderthehoodofthebindingattributeistreatedasaValueExpressionandthelogicexpectedtoobtainamanagedbeaninstanceimplementingtheActionListenerinterfacewheninvokingValueExpression#getValue().However,insteadofagetter,avoidmethodwasinvokedandreturnednothingwhichisinterpretedasnull.So,thelogiccontinuedsilentlyasifthereweresimplynobeaninstanceavailable.

Theactionlistenershaveanadditionalfeatureontopoftheactionattribute.Whenajavax.faces.event.AbortProcessingExceptionisexplicitlythrownfromanactionlistener,thenJSFwillswallowtheexceptionandabortprocessingtheinvokeapplicationphase(fifthphase)andimmediatelyadvancetotherenderresponsephase(sixthphase).Allremainingactionlistenersandtheactionmethod,ifany,willbeskipped.Theswallowedexceptionwon’tendupinanyerrorresponse.Giventhisfact,andthefactthatactionlistenersarealwaysinvokedbeforetheactionmethod,youcould(ab)useittoperformsomeconversionandvalidationbasedonalreadyupdatedmodelvaluesbeforetheactionmethodisinvoked.

publicvoidsomeActionListenerMethod(){

try{

Page 185: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

convertOrValidate(this);

}catch(SomeConversionOrValidationExceptione){

FacesContextcontext=FacesContext.getCurrentInstance()

context.addMessage(null,newFacesMessage(e.getMessage()));

thrownewAbortProcessingException(e);

}

}

publicvoidsomeActionMethod(){

//Won'tbeinvokedwhenAbortProcessingExceptionisthrown.

}

I’msaying“(ab)use”becauseitisessentiallytheresponsibilityofanormalConverterorValidatorimplementationtoperformsuchtask,sothatthemodelvaluesarenotpollutedwithinvalidvalues.However,foralongtimeinJSFitwasnotpossibletoperformconversionorvalidationbasedonmultiplefields.Hencedevelopersstartedusingtheaction(listener)methods,whichisessentiallyaviolationoftheJSFlifecycle.

OnlyinJSF2.3wasanew<f:validateWholeBean>tagintroducedtoperformvalidationonmultiplefields.YoucanreadmoreaboutthisinChapter5.Sothisleavesonlyonereasonablereal-worldusecaseopenforactionlistenermethods:performingconversionbasedononeormoremodelvalues.Oneexamplehasalreadybeendemonstratedinthesection“SelectionComponents”(thecaseofusingmultiple<h:selectBooleanCheckbox>in<ui:repeat>toworkaroundthefactthatthe<h:selectManyCheckbox>generatesanHTMLtable).Anotherexamplewouldbeinvokinganexternalwebservicewiththesuppliedmodelvaluesandobtainingitsresultasa“converted”value.Theactionmethodshouldthenstillbeusedtoexecutebusinessservicelogic.Whentheactionmethodthrowsanexception,therequestwillendupasanHTTP500errorresponse.YoucanfindmoreaboutthisinChapter9.

Apartfromthe<h:commandButton>,JSFofferstwomorecommandcomponents:the<h:commandLink>andthe<h:commandScript>.The

Page 186: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:commandLink>hasbasicallyanidenticallifecycleasthe<h:commandButton>,exceptthatitgeneratesanHTML<a>elementwhichsubmitstheenclosedformwiththehelpofJavaScript.Everywherewhereyouuse<h:commandButton>,itcouldbesubstitutedwith<h:commandLink>.

<h:formid="form">

...

<h:commandLinkid="submit"value="Submit"action="#{bean.submit}"/>

</h:form>

WherebythegeneratedHTMLoutputlooksasfollows:

<formid="form"name="form"method="post"action="projecttest.xhtml"

enctype="application/x-www-form-urlencoded">

<inputtype="hidden"name="form"value="form"/>

...

<scripttype="text/javascript"

src="projectjavax.faces.resource/jsf.js.xhtml?ln=javax.faces">

</script>

<aid="form:submit"href="#"onclick="

mojarra.jsfcljs(

document.getElementById('form'),

{'form:submit':'form:submit'},

''

);returnfalse;">Submit</a>

<inputtype="hidden"name="javax.faces.ViewState"

id="j_id1:javax.faces.ViewState:0"

value="-6936791897896630173:-5064219023156239099"

autocomplete="off"/>

</form>

AndtherenderinginChromebrowser:

Page 187: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

InthegeneratedHTMLoutput,you’llnoticethatitauto-includesthejsf.jsJavaScriptfile.Thiscontains,amongotherthings,thejsfobjectandtheJSFimplementation-specifichelperfunctionswhichareincaseoftheMojarraimplementationputinthemojarraobject.InplainHTML,there’snowaytosubmita<form>usingan<a>element.Hence,someJavaScripthastobethrownintothegame.

InMojarra’sspecificcase,themojarra.jsfcljs()functionwillbeinvokedwiththeparentformasthefirstargument,thecommandcomponent’sclientIDasarequestparameternameandvalueasthesecondargument,andthetargetattributeofthe<h:commandLink>asthethirdargument,ifany.Underthehoodofthemojarra.jsfcljs()functionwillcreate<inputtype="hidden">elementsforeachname/valuepairinthesecondargumentandaddthemtotheformprovidedasthefirstargument,makingsurethatthoseparametersendupapostbackrequest.Thenitwillcreateatemporary<inputtype="submit">button,addittotheform,andinvoketheclick()functiononit,asifyouwouldbeusingaregularsubmitbutton.Finally,itwillremoveallofthosedynamicallycreatedelementsfromtheform.

Thisfunctionisactuallyalsousedbythe<h:commandButton>,butonlywhenyouneedtopassadditionalrequestparametersviaoneormore<f:param>tagsnestedinthecommandcomponent.

<h:formid="form">

...

<h:commandButtonid="submit"value="Submit"action="#{bean.submit}">

<f:paramname="id"value="#{otherBean.id}"/>

</h:commandButton>

</h:form>

GeneratedHTMLoutput:

<formid="form"name="form"method="post"action="projecttest.xhtml"

Page 188: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

enctype="application/x-www-form-urlencoded">

<inputtype="hidden"name="form"value="form"/>

<scripttype="text/javascript"

src="projectjavax.faces.resource/jsf.js.xhtml?ln=javax.faces">

</script>

<inputid="form:submit"type="submit"name="form:submit"

value="Submit"onclick="mojarra.jsfcljs(

document.getElementById('form'),

{'form:submit':'form:submit','id':'42'},

'');returnfalse"/>

<inputtype="hidden"name="javax.faces.ViewState"

id="j_id1:javax.faces.ViewState:0"

value="886811437739939021:6102567809374231851"

autocomplete="off"/>

</form>

RenderinginChromebrowser:

Youcanobtaintheminthemanagedbeanvia@Inject@ManagedProperty.

@Inject@ManagedProperty("#{param.id}")

privateIntegerid;

publicvoidsubmit(){

System.out.println("SubmittedID:"+id);

}

Makesurethatyouimportthe@ManagedPropertyfromtherightpackage.JSFofferstwo,onefromthejavax.faces.beanpackagewhichisdeprecatedsinceJSF2.3andanotheronefromjavax.faces.annotationpackagewhichyoushouldbeusingforCDI.YoualsoneedtomakesurethatyouexplicitlyactivatetheJSF2.3-specificfeatureofCDI-awareELresolversbyhavingatleastonemanagedbeaninthewebapplicationexplicitlyannotated

Page 189: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

with@FacesConfig;otherwisetheCDI-aware@ManagedPropertywouldfailtofindthecurrentinstanceofFacesContextviaCDI.Alsohere,<h:commandButton>issubstitutablewith<h:commandLink>.

There’sanotherwayofpassingparametersaroundviacommandcomponents—thatis,bysimplypassingthemasanactionmethodargument.

<h:formid="form">

...

<h:commandButtonid="submit"value="Submit"

action="#{bean.submit(otherBean.id)}">

</h:commandButton>

</h:form>

Wherebythemodifiedactionmethodlooksasfollows:

publicvoidsubmit(Integerid){

System.out.println("SubmittedID:"+id);

}

Incaseofthe<h:commandButton>,thiswon’tgenerateanyJavaScriptanditthuslooksidenticalaswhenyouaren’tusinganyactionmethodarguments.Thisthusalsomeansthatthe#{otherBean.id}valueisn’tpassedviaHTMLsourcecodebacktotheserverasarequestparameter.Thisinturnmeansthatit’sonlyevaluatedduringthepostbackrequestwhenJSFisabouttoinvoketheactionmethod.Thisinturnmeansthatthe#{otherBean.id}mustatleastbe@ViewScopedinordertobestillavailableinthepostbackrequest.Inotherwords,thisargumentpassingapproachisdefinitelynotexchangeablewiththe<f:param>tagapproachwherebybothbeanscanbejust@RequestScoped.

ThelastcommandcomponentofferedbythestandardJSFcomponentsetis<h:commandScript>.ThisisnewsinceJSF2.3.Itallowsyoutoinvokea

Page 190: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

managedbeanactionmethodbyjustcallinganamedJavaScriptfunctionfromyourownscript.ThepostbackrequestwillalwaysbeperformedviaAjax.

<h:formid="form">

<h:commandScriptid="submit"name="invokeBeanSubmit"

action="#{bean.submit}">

</h:commandScript>

</h:form>

GeneratedHTMLoutput:

<formid="form"name="form"method="post"action="projecttest.xhtml"

enctype="application/x-www-form-urlencoded">

<inputtype="hidden"name="form"value="form"/>

<scripttype="text/javascript"

src="projectjavax.faces.resource/jsf.js.xhtml?ln=javax.faces">

</script>

<spanid="form:submit">

<scripttype="text/javascript">

varinvokeBeanSubmit=function(o){

varo=(typeofo==='object')&&o?o:{};

mojarra.ab('form:submit',null,'action',0,0,{'params':o});

}

</script>

</span>

<inputtype="hidden"name="javax.faces.ViewState"

id="j_id1:javax.faces.ViewState:0"

value="3568384626727188032:3956762118801488231"

autocomplete="off"/>

</form>

IthasnovisibleHTMLrenderinginwebbrowsers.Inthegeneratedscript,you’llseethatithasgeneratedafunctionvariablewiththesamenameasspecifiedinthenameattribute.Inthisexample,it’sindeedintheglobalscope.AsthisisconsideredpoorpracticeintheJavaScriptcontext(“globalnamespacepollution”),you’dbetterprovideanamespacedfunctionname.Thisonlypre-

Page 191: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

requiresthatyou’vealreadydeclaredyourownnamespacesomewherebeforeintheHTMLdocument,usuallyviaaJavaScriptfileinthe<head>element.Thefollowingexamplesimplifiesitwithaninlinescript:<h:head>...

<script>varmynamespace=mynamespace||{};</script>

</h:head>

<h:body>

<h:formid="form">

<h:commandScriptid="submit"name="mynamespace.invokeBeanSubmit"

action="#{bean.submit}">

</h:commandScript>

</h:form>

</h:body>

Comingbacktothegeneratedfunctionvariable,you’llalsoseethatitacceptsanobjectargumentandpassesitthroughas“params”propertyofthelastobjectargumentoftheMojarra-specificmojarra.ab()function.Thathelperfunctionwill,underthehoodofthemojarra.ab()function,prepareandinvokethejsf.ajax.request()functionofthestandardJSFJavaScriptAPI.Inotherwords,youcanpassJavaScriptvariablestoamanagedbeanactionmethodthisway.Theyareinjectablevia@ManagedPropertythesamewayasifyouwereusing<f:param>.ThefollowingexampledemonstratestheJavaScriptcallwithhard-codedvariablesinaJavaScriptobject,butyoucanofcourseobtainthosevariablesfromanywhereelseinJavaScriptcontext:varparams={

id:42,

name:"JohnDoe",

email:"[email protected]"

};

invokeBeanSubmit(params);

Page 192: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Backingbeanclass:

@Inject@ManagedProperty("#{param.id}")

privateIntegerid;

@Inject@ManagedProperty("#{param.name}")

privateStringname;

@Inject@ManagedProperty("#{param.email}")

privateStringemail;

publicvoidsubmit(){

System.out.println("SubmittedID:"+id);

System.out.println("Submittedname:"+name);

System.out.println("Submittedemail:"+email);

}

The<h:commandScript>canalsobeusedtodeferthepartialrenderingofanHTMLdocumenttothewindowloadevent.Toachievethis,simplysettheautorunattributetotrueandspecifytheclientIDofthetargetcomponentintherenderattribute.Thefollowingexampleloadsandrendersadatatableonlywhenthepagehasfinishedloadingintheclientside:

<h:panelGrouplayout="block"id="lazyPersonsPanel">

<h:dataTablerendered="#{notemptybean.lazyPersons}"

value="#{bean.lazyPersons}"var="person">

<h:column>#{person.id}</h:column>

<h:column>#{person.name}</h:column>

<h:column>#{person.email}</h:column>

</h:dataTable>

</h:panelGroup>

<h:formid="form">

<h:commandScriptid="loadLazyPersons"name="loadLazyPersons"

autorun="true"action="#{bean.loadLazyPersons}"

render=":lazyPersonsPanel">

</h:commandScript>

</h:form>

Page 193: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Wherebythebackingbeanlooksasfollows:

@Named@RequestScoped

publicclassBean{

privateList<Person>lazyPersons;

@Inject

privatePersonServicepersonService;

publicvoidloadLazyPersons(){

lazyPersons=personService.getAll();

}

publicList<Person>getLazyPersons(){

returnlazyPersons;

}

}

AndthePersonentitylooksasfollows:publicclassPerson{

privateLongid;

privateStringname;

privateStringemail;

//Add/generategettersandsetters.

}

Notethatanypersistenceframework-specificannotations,suchasJPA’s@Entityand@Id,andtheactualimplementationofPersonService,areomittedforclarity.Thoseare,namely,irrelevanttoanyfront-endframework,suchasJSF.

Comingbacktotheavailablecommandcomponents,itmayspeakforitselfthat<h:commandScript>isonlyusefulinordertobeabletoinvokeaJSF

Page 194: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

managedbeanactionmethodusingnativeJavaScript,generallyduringaspecificHTMLDOM(DocumentObjectModel)event.However,both<h:commandLink>and<h:commandButton>seemtodoexactlythesamething;onlythevisualpresentationisdifferent.Onerendersalinkandtheotherrendersabutton.Theuserexperience(UX)consensusisthatabuttonmustbeusedtosubmitaform,andalinkmustbeusedtonavigatetoanotherpageorjumptoananchor.Usingalinktosubmitaformisthereforenotalwaysconsideredthebestpractice.It’sonlyusefulwhenyou’dliketosubmitanHTMLformusinganiconorimage.Forallothercases,useanormalbutton.ThefollowingexampleshowshowacommandlinkcanbeusedonaFontAwesomeicon:

<h:commandLinkid="delete"action="#{bean.delete}">

<iclass="fafa-trash"/>

</h:commandLink>

NavigationSometimes,you’dliketonavigatetoadifferentJSFpagewhenacertainformhasbeensuccessfullysubmitted—forexample,fromtheloginpagetotheuserhomepage,asdemonstratedinthesection“LabelandMessageComponents,”orfromthedetailpagebacktothemasterpage.

Historically,navigationtargetsmustbedefinedseparatelyin<navigation-rule>entriesinthefaces-config.xmlwhichthendoesthejobbasedontheStringreturnvaluefromtheactionmethodofUICommandcomponents.Thisapproachturnsouttobequitecumbersomeinthelongterm,andnotterriblyusefulforHTML-basedwebapplications.Thisideawasmoreorlessderivedfromdesktop-orientedapplications.Hence,JSF2.0introducedthe“Implicitnavigation”feature,whichallowsyoutodefinethenavigationtargetdirectlyintheStringreturnvalueitself.Inotherwords,insteadofthefollowingactionmethod:publicStringsomeActionMethod(){

Page 195: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

//...

return"someOutcome";

}

Andthefollowingfaces-config.xmlentry:<navigation-rule><navigation-case>

<from-outcome>someOutcome</from-outcome>

<to-view-id>/otherview.xhtml</to-view-id>

</navigation-case>

</navigation-rule>

youcouldjustdoasfollowsintheactionmethod:

publicStringsomeActionMethod(){

//...

return"/otherview.xhtml";

}

Youcouldevenleaveoutthedefaultsuffixoftheviewtechnologyyou’reusing.

publicStringsomeActionMethod(){

//...

return"/otherview";

}

Youcanforcearedirectbyappendingafaces-redirect=truequeryparameter.

publicStringsomeActionMethod(){

//...

return"/otherview?faces-redirect=true";

}

Returningnullwouldreturntotheverysameviewfromwheretheformwassubmitted.Inotherwords,theenduserwouldstayinthesamepage.Itis

Page 196: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

cleaner,however,todeclaretheactionmethodasvoid.

publicvoidsomeActionMethod(){

//...

}

Comingbacktotheredirectapproach,thisisalsoknownas“Post-Redirect-Get”pattern andmakesamajordifferencewithregardtobookmarkabilityandavoidingdoublesubmits.WithoutaredirectafteraPOSTrequestonaJSFform,theURL(uniformresourcelocator)inthewebbrowser’saddressbarwouldn’tchangetotheURLofthetargetpagebutwouldjuststaythesame.That’scausedbythenatureofthe“postback”:submittingtheformbacktotheverysameURLfromwhichthepagewiththeformwasserved.WhenJSFisinstructedtonavigatetoadifferentviewwithoutaredirect,thenitwillbasicallybuildandrenderthetargetpagedirectlytotheresponseofthecurrentpostbackrequest.

Thisapproachhasdisadvantages.OneisthatrefreshingthepageinthewebbrowserwouldcausethePOSTrequesttobere-executed,andthusperformaso-calleddoublesubmit.Thiswouldpotentiallypollutethedatastoreinthebackendwithduplicateentries,particularlyiftheinvolvedrelationaltabledoesn’thaveproperuniqueconstraintsdefined.Anotherdisadvantageisthatthetargetpageisn’tbookmarkable.TheURLcurrentlyinthebrowser’saddressbarbasicallyrepresentsthepreviouspage.Youwon’tgetthetargetpagebackbybookmarking,copy/pasting,and/orsharingtheURLandthenopeningitinanewbrowserwindow.

WhenJSFisinsteadinstructedtonavigatetoadifferentviewwitharedirect,thenitwillbasicallyreturnaverysmallHTTPresponsewithastatusof302andaLocationheaderwiththeURLofthetargetpagetherein.Whenthewebbrowserretrievessucharesponse,itwillimmediatelyfireabrand-newGETrequestontheURLspecifiedintheLocationheader.ThisURLisreflectedinthewebbrowser’saddressbarandisthusbookmarkable.Also,refreshingthepagewouldonlyrefreshtheGETrequestandthereforenotcauseadouble

2

Page 197: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

submit.

AjaxifyingComponentsAsyouhavenoticedin<h:commandScript>inthesection“CommandComponents,”JSFiscapableoffiringAjaxrequestsandperformingpartialrenderingaswell.ThiscapabilitywasintroducedinJSF2.0forthefirsttimewiththe<f:ajax>tag.ThistagcanbenestedinanycomponentimplementingClientBehaviorHolderinterface,oritcanbewrappedaroundagroupofcomponentsimplementingthisinterface.InthestandardJSFcomponentset,almostallHTMLcomponentsimplementClientBehaviorHolderaswell.IfyouconsulttheClientBehaviorHolderJavadoc, thenyou’llfindthefollowinglist:AllKnownImplementingClasses:

HtmlBody,HtmlCommandButton,HtmlCommandLink,HtmlDataTable,HtmlForm,

HtmlGraphicImage,HtmlInputFile,HtmlInputSecret,HtmlInputText,

HtmlInputTextarea,HtmlOutcomeTargetButton,HtmlOutcomeTargetLink,

HtmlOutputLabel,HtmlOutputLink,HtmlPanelGrid,HtmlPanelGroup,

HtmlSelectBooleanCheckbox,HtmlSelectManyCheckbox,HtmlSelectManyListbox,

HtmlSelectManyMenu,HtmlSelectOneListbox,HtmlSelectOneMenu,HtmlSelectOneRadio,

UIWebsocket

Thatarethusthe<h:body>,<h:commandButton>,<h:commandLink>,<h:dataTable>,<h:form>,<h:graphicImage>,<h:inputFile>,<h:inputSecret>,<h:inputText>,<h:inputTextarea>,<h:button>,<h:link>,<h:outputLabel>,<h:outputLink>,<h:panelGrid>,<h:panelGroup>,<h:selectBooleanCheckbox>,<h:selectManyCheckbox>,<h:selectManyListbox>,<h:selectManyMenu>,<h:selectOneListbox>,<h:selectOneMenu>,<h:selectOneRadio>and<f:websocket>.

You’llseethatallvisibleinput,select,andcommandcomponentsarecoveredaswell.

3

Page 198: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Arequirementofthe<f:ajax>isthattheClientBehaviorHoldercomponentisnestedin<h:form>,andthat<h:head>isbeingusedinthetemplate.The<h:form>basicallyenablesJavaScripttoperformapostbackrequestwiththerightJSFviewstateassociated.The<h:head>basicallyenables<f:ajax>toautomaticallyincludethenecessaryjsf.jsJavaScriptfilewhichcontains,amongothers,themandatoryjsf.ajax.request()function.

<h:headid="head">

<title>f:ajaxdemo</title>

</h:head>

<h:body>

<h:formid="form">

<h:inputTextid="text"value="#{bean.text}">

<f:ajax/>

</h:inputText>

<h:commandButtonid="submit"value="Submit"

action="#{bean.submit}">

<f:ajaxexecute="@form"/>

</h:commandButton>

</h:form>

</h:body>

GeneratedHTMLoutput:

<headid="head">

<title>f:ajaxdemo</title>

<scripttype="text/javascript"

src="projectjavax.faces.resource/jsf.js.xhtml?ln=javax.faces">

</script>

</head>

<body>

<formid="form"name="form"method="post"

action="projecttest.xhtml"

enctype="application/x-www-form-urlencoded">

<inputtype="hidden"name="form"value="form"/>

Page 199: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<inputid="form:text"type="text"name="form:text"

onchange="mojarra.ab(this,event,'valueChange',0,0)"/>

<inputid="form:submit"type="submit"name="form:submit"

value="Submit"onclick="mojarra.ab(

this,event,'action','@form',0);returnfalse;"/>

<inputtype="hidden"name="javax.faces.ViewState"

id="j_id1:javax.faces.ViewState:0"

value="6345708413515990903:-8460061657159853996"

autocomplete="off"/>

</form>

</body>

RenderinginChromebrowser(withnewlinesadded)isidenticalaswithoutAjax:

InthegeneratedHTMLoutput,you’llseethatthejsf.jsJavaScriptfilecontainingthenecessaryJSFAjaxAPIisauto-includedintheHTMLhead.You’llalsonoticethat<f:ajax>in<h:inputText>hasgeneratedanadditionalonchangeattribute,andin<h:commandButton>anadditionalonclickattribute,bothdefiningsomeJSFimplementation-specificJavaScriptcoderesponsibleforperformingtheAjaxrequest.

JSFspecifiestwointernalAjaxeventtypes:valueChangeandaction.Thosearethedefaulteventtypesincase<f:ajax>doesn’thavetheeventattributespecified.When<f:ajax>isattachedtoacomponentimplementingtheEditableValueHolderinterface,thenthedefaulteventtypebecomesvalueChange.ForcomponentsimplementingtheActionSourceinterface,thisisaction.ForallotherClientBehaviorHoldercomponents,thedefaulteventisclick.TheactualgeneratedHTMLDOMeventtypeforthoseinternaleventtypesdependsonthecomponentandtheassociatedrenderer.

Incaseoftext-basedinputcomponentsanddrop-down-andlistbox-based

Page 200: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

selectioncomponents,thedefaultHTMLDOMeventtypefor<f:ajax>is"change".Incaseofradio-andcheckbox-basedselectioncomponentsandcommandcomponents,thisis"click".YoucanseethisinthegeneratedHTMLoutput,whichcanbeoverriddenbyexplicitlyspecifyingtheeventattributeonthe<f:ajax>tag.

<h:inputText...>

<f:ajaxevent="blur"/>

</h:inputText>

TheaboveexamplewillgeneratetheJavaScriptcodeintheonblurattributeinsteadoftheonclickattribute.ThesupportedvaluesfortheeventattributedependonthetargetClientBehaviorHoldercomponent.TheycanbefoundintheVDLdocumentationofthecomponentofinterest.Allon[event]attributesaredefinedoverthere.Whenyouremovethe“on”prefixonthem,thenyouhavealistofsupportedeventtypes.Forexample,theVDLdocumentationof<h:inputText> indicatesthatthefollowingeventtypesaresupported:

blur,change,click,dblclick,focus,keydown,keypress,keyup,mousedown,mousemove,mouseout,mouseover,mouseup,select

WhenthedesiredDOMeventtypeoccursontheclientsideandtriggerstheassociatedJSFimplementation-specificJavaScriptcodedefinedintheon[event]attribute,thenultimatelythejsf.ajax.request()functionofthestandardJSFJavaScriptAPIwillbeinvoked.Itwillprepareabunchofpredefinedpostbackparametersofwhichjavax.faces.sourceandjavax.faces.behavior.eventarethemostimportantones.TheformerspecifiestheclientIDofthesourcecomponent,essentiallythevalueofthis.idinJavaScriptcontext.Thelatterspecifiestheeventtype,essentiallythevalueofevent.typeinJavaScriptcontext.You’llhaveguessedthattheyarederivedfromthefirsttwoargumentspassedtotheMojarra-specific

4

Page 201: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

mojarra.ab()functionasvisibleinthegeneratedHTMLoutput.Oncefired,theAjaxrequestwillrunthroughtheJSFlifecyclealmostthe

samewayasanon-Ajaxrequest.Therestoreviewphase(firstphase),processvalidationsphase(thirdphase),updatemodelvaluesphase(fourthphase),andinvokeapplicationphase(fifthphase)areidentical.Theapplyrequestvaluesphase(secondphase)isslightlydifferent.Itwillonlydecodethecomponentsthatarecoveredbytheexecuteattributeofthe<f:ajax>tag,whichdefaultsto@this(“thecurrentcomponent”).Therenderresponsephase(sixthphase)iscompletelydifferent.InsteadofgeneratingawholeHTMLdocument,itgeneratesaspecialXMLdocumentwhichcontainsonlythegeneratedHTMLoutputofcomponentswhicharecoveredbytherenderattributeofthe<f:ajax>tag,whichdefaultsto@none(“noonecomponent”).

Theexecuteandrenderattributesacceptaspace-separatedcollectionofcomponentsearchexpressions.ThiscanrepresentaclientIDrelativetotheclosestNamingContainerparent,oranabsoluteclientIDwhichisalwaysrelativetotheUIViewRoot,orstandardorcustomsearchkeywords,orchainedcombinationsthereof.SeeChapter12foranin-depthexplanationofthem.Fornow,weonlyneedtoknowaboutthestandardsearchkeywords@this,@form,[email protected],the@formkeywordreferstotheclosestparentcomponentoftheUIFormtype,suchas<h:form>.

Duringtheapplyrequestvaluesphase(secondphase)oftheAjaxrequest,JSFwill,foreachcomponentcoveredbytheexecuteattributeofthe<f:ajax>taginadditiontothedefaultdecodeprocess,alsocheckifthejavax.faces.sourcerequestparameterequalsthecurrentcomponent’sclientID.Ifso,thenJSFwillqueuetheAjaxBehaviorEventfortheinvokeapplicationphase(fifthphase).UnderthehoodofqueueingtheAjaxBehaviorEvent,itboilsdowntothefollowinglogic:FacesContextcontext=FacesContext.getCurrentInstance();

ExternalContextexternalContext=context.getExternalContext();

Map<String,String>formData=externalContext.getRequestParameterMap();

Page 202: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

StringclientId=component.getClientId(context);

Stringsource=formData.get("javax.faces.source");

Stringevent=formData.get("javax.faces.behavior.event");

if(clientId.equals(source)){

component.getClientBehaviors().get(event)

.forEach(behavior->component.queueEvent(

newAjaxBehaviorEvent(context,component,behavior)));

}

Here,theClientBehaviorbasicallyrepresentsthedefinitionofthe<f:ajax>tag.Basedonthislogic,youwillconcludethatyoucanhavemultiple<f:ajax>tagsattachedintheverysamecomponent,evenonsameeventtypes.Theadvantageisthatyoucan,ifnecessary,registermultipleAjaxbehaviorlistenersontheverysameeventtype.

<h:inputTextid="foo"...>

<f:ajaxlistener="#{bean.onchangeFoo}"/>

<f:ajaxlistener="#{otherBean.onchangeFoo}"/>

</h:inputText>

ThoseAjaxbehaviorlistenermethodswillthusbeinvokedduringtheinvokeapplicationphase(fifthphase);ofcourse,onlywhenthere’snoconversionorvalidationerrorduringtheprocessvalidationsphase(thirdphase).Incaseofcommandcomponents,thoseAjaxbehaviorlistenermethodswillalwaysbeinvokedbeforetheactionlistenermethodsandtheactionmethod.Regardlessofthetargetcomponent,theAjaxbehaviorlistenermethodmustbeapublicvoidmethodwhichcanoptionallytaketheAjaxBehaviorEventargument.

publicvoidonchangeFoo(AjaxBehaviorEventevent){

//...

Page 203: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

Thisgivesyou,ininputandselectcomponents,theopportunitytoperformsomebusinesstaskonaspecificAjaxevent.Mostoccurringreal-worldexamplesinvolvepreparinganotherbeanpropertywhichinturngetsrenderedinanothercomponent.Thinkofcascadingdrop-downmenuswhereintheavailableitemsofthechilddrop-downmenudependontheselecteditemoftheparentdrop-down.Inactioncomponents,<f:ajaxlistener>isn’tterriblyuseful.Youalreadyhavethepossibilitytoperformthebusinesstaskinactionlistenerand/oractionmethod.Youcanjustcontinueusingthemevenwhenhaving<f:ajax>attached.

Duringtherenderresponsephase(sixthphase)oftheAjaxrequest,JSFwillforeachcomponentcoveredbytherenderattributeofthe<f:ajax>taggenerateaXML<update>elementwhichcontainsthegeneratedHTMLoutputofonlytheparticularcomponentandallofitschildren,ifany.Thejsf.ajax.response()functionofthestandardJSFJavaScriptAPI,whichisbythejsf.ajax.request()registeredasAjaxcallbackfunction,willextracttheidattributeofthe<update>element,whichrepresentstheclientIDofthetargetcomponent,andobtainviaJavaScript’sdocument.getElementById()ontheclientIDtheconcreteHTMLelementandreplaceitintheHTMLDOMtreewiththecontentsofthe<update>element.

Followingisanexampleofaformwithonerequiredinputfieldhavingamessageattached,andacommandbuttonwhichexplicitlytargetsthemessagecomponent:

<h:formid="form">

<h:inputTextid="text"value="#{bean.text}"required="true"/>

<h:messageid="m_text"for="text"/>

<br/>

<h:commandButtonid="submit"value="Submit"action="#{bean.submit}">

<f:ajaxexecute="@form"render="m_text"/>

Page 204: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</h:commandButton>

</h:form>

Figure4-2showshowChromepresentstheAjaxresponseaftersubmittingtheformwiththeinputfieldnotfilledout.It’sabigone-liner,soit’sscrolledabitsoitstartsatthe<update>elementofinterest.ItcontainsthegeneratedHTMLoutputofthe<h:messageid="m_text">component.

Figure4-2 ChromeDeveloperTools—Network—Response

IfyouscrollfurtherintheXMLresponse,thenyou’llalsonoticean<updateid="j_id1:javax.faces.ViewState:0">elementcontainingthevalueofthejavax.faces.ViewStatehiddeninputelement.ThisisimportantforJSFinordertomaintaintheviewstateacrossAjaxrequests.WhentherenderattributehappenstocoveraUIFormcomponent,thenthejavax.faces.ViewStatehiddeninputelementcurrentlyintheHTMLdocumentwillbasicallybecompletelywipedoutduringthereplacementoftheelementintheHTMLDOMtreewithcontentsofthe<update>elementoftheAjaxresponse.

Themissingjavax.faces.ViewStatehiddeninputelementwilleventuallybeappendedtoevery<formmethod="post">ofthecurrent

Page 205: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

UIViewRoot.Thisapproachisactuallybydesignfortworeasons:(1)becausetheviewstatevaluecouldchangeacrossAjaxrequestsandthereforetheexistingformscurrentlyintheHTMLdocumenthavetobeupdatedtocatchupthischange,justincasethoseformsarenotcoveredbytherenderattribute;and(2)becausethevalueofthejavax.faces.ViewStatehiddeninputfieldcangetquitelargewhentheJSFstatesavingmethodisexplicitlysetto“client”andthusotherwiserenderaninefficientAjaxresponsewhentherenderattributehappenstocovermultipleforms.

NavigationinAjaxInUICommandcomponentswithaproperlydefinedactionmethod,it’snotdifferent.However,sometimesyou’dliketoperformnavigationinanAjaxlistenerattachedtonUIInputcomponent.Therearereasonablereal-worldusecasesforthis.However,theUIInputclassdoesn’tsupportdefininganactionmethodand<f:ajaxlistener>doesn’tsupportreturninganavigationoutcome.Therefore,youronlyoptionistoperformthenavigationprogrammatically.Thiscanbedoneintwoways.Thefirstwayistousethejavax.faces.application.NavigationHandler.

publicvoidajaxListener(AjaxBehaviorEventevent){

//...

Stringoutcome="/otherview?faces-redirect=true";

FacesContextcontext=FacesContext.getCurrentInstance();

Applicationapplication=context.getApplication();

NavigationHandlerhandler=application.getNavigationHandler();

handler.handleNavigation(context,null,outcome);

}

Thesecondwayistousethejavax.faces.context.ExternalContext#redirect().

5

6

Page 206: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicvoidajaxListener(AjaxBehaviorEventevent)throwsIOException{

//...

Stringpath="/otherview.xhtml";

FacesContextcontext=FacesContext.getCurrentInstance();

ExternalContextexternalContext=context.getExternalContext();

Stringuri=externalContext.getRequestContextPath()+path;

externalContext.redirect(uri);

}

Thereareseveraldifferences.Mostimportant,theNavigationHandlercandealwithimplicitnavigationoutcomevalues,butExternalContext#redirect()canonlydealwithactualpathsandrequiresmanualprefixingoftherequestcontextpathwhenitconcernsawebapplicationresource.However,itcantakebasicallyanyURI,suchasanexternalURLasinexternalContext.redirect("http://example.com"),whereastheNavigationHandlercan’tdealwiththem.

GETformsJSFhasnoconceptof“GETforms,”butyoucanjustuseplainHTMLforthis.JSFsupportsprocessingGETrequestparametersandinvokingmanagedbeanactionsonGETrequests.Forthis,<f:viewParam>and<f:viewAction>canbeused.Theymustbeplacedin<f:metadata>whichinturncanonlybedeclaredinthetop-levelpage.So,whenusingtemplating,itmustbedeclaredinthetemplateclientandyoucan’tdeclareitinthemastertemplate.Inotherwords,<f:metadata>cannotbesharedacrosstemplateclients.

Technically,thelocationof<f:metadata>intheviewdoesn’tmatter,aslongasit’sinthetop-levelpage.Mostself-documentingwouldbetoputitintheverytopoftheview,directlyaftertheroottag.

<!DOCTYPEhtml>

<htmllang="en"

Page 207: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:f="http://xmlns.jcp.org/jsf/core"

xmlns:h="http://xmlns.jcp.org/jsf/html"

>

<f:metadata>

...

</f:metadata>

<h:head>

...

</h:head>

<h:body>

...

</h:body>

</html>

Whenusingtemplating,giveititsowntemplatedefinition.

<ui:compositiontemplate="WEB-INFtemplates/layout.xhtml"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:f="http://xmlns.jcp.org/jsf/core"

xmlns:h="http://xmlns.jcp.org/jsf/html"

>

<ui:definename="metadata">

<f:metadata>

...

</f:metadata>

</ui:define>

<ui:definename="content">

...

</ui:define>

</ui:composition>

No,youcan’tput<f:metadata>inthemastertemplateandkeep<f:viewParam>and<f:viewAction>inthetemplateclient.Thisisatechnicallimitation.Thebestyoucandoistocreateacustom<f:event>type

Page 208: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

whichrunsaftertheinvokeapplicationphase(fifthphase)andthendeclareitinthemastertemplate.Anexampleisgiveninthesection“CreateCustomComponentEvent”inChapter3.

The<f:viewParam>tagisbackedbytheUIViewParametercomponentwhichinturnextendsfromUIInputsuperclass.Thismeansthatitbehavesalmostexactlylike<h:inputText>,butthenforGETparameters.Thesubtledifferencesarefoundintheprocessvalidationsphase(thirdphase).Bydefault,anemptyparameterwouldskipanycustomvalidatorsandbeanvalidation.Forexample,the@NotNullbeanvalidationannotationwillonlyworkwhenthecontextparameterjavax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS

_NULLisexplicitlysettotrueinweb.xml.Theotherdifferenceisintherenderresponsephase(sixthphase).Basically,itrendersabsolutelynothing.

The<f:viewAction>tagisbackedbytheUIViewActioncomponentwhichinturnimplementstheActionSourceinterface.Thismeansthatitbehavesalmostexactlylike<h:commandButton>,butthenforGETrequests.Ofcourse,youcouldalsousea@PostConstructannotatedmethodona@ViewScopedmanagedbeanforperforminglogiconGETrequests,buttheproblemisthatitwouldrundirectlyafterthemanagedbeaninstanceiscreated,when<f:viewParam>hasn’tevenhadachancetorun.<f:viewAction>willbeinvokedduringtheinvokeapplicationphase(fifthphase),afterthemodelvaluesareupdated.ItevensupportsreturningaStringrepresentinganavigationoutcome,whichwillthenbehaveasaredirect.

Followingisanexampleofasearchform:Faceletsfile/search.xhtml:<f:metadata>

<f:viewParamid="query"name="query"value="#{search.query}"/>

<f:viewActionaction="#{search.onload}"/>

</f:metadata>

<h:body>

Page 209: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<form>

<labelfor="query">Query</label>

<inputtype="text"name="query"

value="#{emptysearch.query?param.query:search.query}">

</input>

<inputtype="submit"value="Search"/>

<h:messagefor="query"/>

</form>

<h:dataTableid="results"rendered="#{notemptysearch.results}"

value="#{search.results}"var="result">

<h:column>#{result.name}</h:column>

<h:column>#{result.description}</h:column>

</h:dataTable>

</h:body>

Backingbeanclasscom.example.project.view.Search:@Named@RequestScoped

publicclassSearch{

privateStringquery;

privateList<Result>results;

@Inject

privateSearchServicesearchService;

publicvoidonload(){

results=searchService.getResults(query);

}

//Add/generategettersandsettershere.

//Notethatresultsdoesn'tneedasetter.

Page 210: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

IntheFaceletsfilethereareacoupleofthingstonoticeapartfromtheplainHTMLformapproach.Thevalueattributeofthetextinputdisplays#{param.query}when#{search.query}isempty,becausethesubmittedvaluewouldotherwisenotshowupatallwhenthere'saconversionorvalidationerroron<f:viewParam>.#{param}isactuallyanimplicitELobjectreferringtherequestparametermap.#{param.query}basicallyprintsthevalueoftherequestparameterwiththename“query”.PleasenotethatthisconstructofthevalueattributeisinvalidforJSFinputcomponents.Itwouldthrowajavax.el.PropertyNotWritableExceptionduringtheupdatemodelvaluesphase(fourthphase),and,moreover,itisalreadydoingtheverysamelogicunderthehoodofthe<f:viewParam>.

<h:message>canbeattachedto<f:viewParam>.Inthisspecificconstruct,however,it’snotreallyused.Onlywhenyouaddaconverterorvalidatorto<f:viewParam>,forexample,by<f:viewParam...required="true">wouldyouseetheerrormessagein<h:message>,andthen<f:viewAction>won’tbeinvoked.

Now,whenyouopenthepageandsubmittheform,thesubmittedvaluewillappearasaquerystringintheURLasin/search.xhtml?query=jsf.Thisisbookmarkableandre-executableeverytimeyouopentheURL.

StatelessFormsStatesavingisparticularlyhelpfulindynamicallymanipulatedformswhichuseAjaxtoconditionallyrenderpartsoftheform,suchascascadingdrop-downmenusandsecondaryinputfields.JSFremembersthestateoftheformacrossAjaxpostbacksonthesameview.Generally,itisthoseformswhereyouabsolutelyneedaview-scopedmanagedbeaninsteadofarequest-scopedmanagedbean.

Whenyourwebsitehas"public"and"private"sections,you'dliketo

Page 211: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

postponetheHTTPsessioncreationasmuchaspossibleuntiltheenduserhasactuallyloggedin.Thiswayrobotswon’ttriggertheunnecessarycreationoftheHTTPsession.However,ifyouhaveastandardJSFloginforminthepublicsection,theHTTPsessionwouldalreadybecreatedbyjustaccessingthatpage.Thisisanunnecessarycostintermsofservermemoryiftheformhasbasicallynodynamicstateofitsownandistiedtoarequest-scopedmanagedbean.Youcouldconsiderusingclient-sidestatesavinginstead,butthiswillaffecttheentirewebsiteandithasacostintermsofnetworkbandwidthandCPU(centralprocessionunit)power.True,thecostisnegligibleifyouhavestate-of-the-arthardware,butit'snotnegligibleifyouhavealotofvisitorsand/orrelativelypoorhardware.

Incaseofstaticformstiedtoarequest-scopedbean,suchasasimpletwo-fieldloginformwhichcantheoreticallysafelybeentirelyclearedoutoneverypostback,thentheviewstatedoesn’tnecessarilyneedtobesaved.Thiscanbeachievedbysettingthetransientattributeof<f:view>totrue.

<f:viewtransient="true">

<h:formid="login">

...

</h:form>

</f:view>

ThiswayJSFwon’tcreateanyviewstateandthejavax.faces.ViewStatehiddeninputfieldwillreceiveafixedvalueof“stateless”.Notethatthisaffectstheentireviewandthere’snowaytotogglethisforonlyaspecificform.JSFcurrentlydoesnotsupportconfiguringthestatesavingmethodonaperformbasis.Also,statelessnesshasanadditionaldisadvantageinthatit'stheoreticallyeasiertoperformaCSRF(crosssiterequestforgery)attackifthere'sanopenXSShole.(Seealsothesection“CrossSiteRequestForgeryProtection”inChapter13.)Fortunately,withJSFit'salreadyveryhardtoaccidentallyintroduceaXSShole.TheonlywaytogetaXSShole

Page 212: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

istouse<h:outputTextescape="false">toredisplayuser-controlleddata.

Footnotes1http://balusc.omnifaces.org/2015/10/custom-layout-with-hselectoneradio-

in.html.

2https://en.wikipedia.org/wiki/Post/Redirect/Get.

3https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/behavior/ClientBehaviorHolder.html.

4

https://javaserverfaces.github.io/docs/2.3/vdldocs/facelets/h/inputText.

html.

5https://javaee.github.io/javaee-

spec/javadocs/javax/faces/application/NavigationHandler.html.

6https://javaee.github.io/javaee-

spec/javadocs/javax/faces/context/ExternalContext.html#redirect-

java.lang.String-.

Page 213: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1) (2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_5

5.ConversionandValidation

BaukeScholtz andArjanTijms

Willemstad,Curaçao Amsterdam,Noord-Holland,TheNetherlands

Atitscore,JSF(JavaServerFaces)asanHTMLform-basedMVC(Model-View-Controller)frameworkbasicallyneedstoconvertbetweenJavaobjects(entities,beans,valueobjects,datatransferobjects,andwhatnot)andcharactersequences(strings)allthetime.TheHTTPrequestisbasicallybrokendownintoplainvanillastringsrepresentingheadersandparameters,notasJavaobjects.TheHTTPresponseisbasicallywrittenasonebigsequenceofcharactersrepresentingHTMLorXML,notassomesortofserializedformofaJavaobject.However,theaverageJavamodelbehindaJSFpagedoesn’tnecessarilycontainStringpropertieseverywhere.ThatwoulddefeatthestrongtypednatureofJava.ThisiswhereConverterscomeintothepicture:convertingbetweenobjectsinmodelandstringsinview.

Beforeupdatingthemodelvalueswithfreshlysubmittedand,ifnecessary,convertedvalues,youwouldofcourseliketovalidatewhethertheyconformtothebusinessrulesofthewebapplicationand,ifnecessary,presentendusersan

1 2

Page 214: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

informativeerrormessagesothattheycanfixanyerrorsthemselves.Usually,thebusinessrulesarealreadyverywelldefinedinthedatastore,suchasarelationaldatabasemanagementsystem.Adecentlydesigneddatabasetablealreadyhasstrictconstraintsonthedatatype,maximumsize,nullability,anduniqueness.Youasafront-enddevelopershouldmakeabsolutelysurethatthesubmittedandconvertedvaluescanbeinsertedinthedatabasewithouterrors.

If,forexample,thee-mailaddresscolumnisconstrainedasauniqueandnon-nullablecolumnwithamaximumsizeof254characters,thenyoushouldmakesurethatthesubmittedvalueisvalidatedassuchbeforeinsertingitinthedatabase.Otherwise,thedatabaseinsertwouldthrowsomeexceptionwhichisgenerallycumbersometobreakdownintodetailedinformationinordertotelltheenduserabouttheexactmistake.ThisiswhereValidatorscomeintothepicture:validatingsubmitted(andconverted)valuesbeforeupdatingthemodel.

StandardConvertersJSFhas,fromthebeginning,providedabunchofstandardconvertersoutthebox.MostofthemevendotheirjobfullytransparentlybasedontheJavatypeofthemodelproperty.Theyareallavailableinthejavax.faces.convertpackage andtheyallimplementtheConverter<T>interface.Table5-1providesanoverviewofthem.

Table5-1 StandardConvertersProvidedbyJSF

Converterclass

ConverterID

Convertertag

Valuetype

Sinc

1

Page 215: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

e

BigDecimalConverter

javax.faces.BigDecimal

n/a

java.math.BigDecimal

1.0

BigIntegerConverter

javax.faces.BigInteger

n/a

java.math.BigInteger

1.0

BooleanConverter

javax.faces.Boolean

n/a

boolean/java.lang.Boolean

1.0

ByteConverter

javax.faces.Byte

n/a

byte/java.lang.Byte

1.0

CharacterConverter

javax.faces.Character

n/a

char/java.lang.Character

1.0

DateTimeConverter

javax.faces.DateTime

<f:convertDateTime>

java.util.Datejava.time.LocalDatejava.time.LocalTimejava.time.OffsetTimejava.time.LocalDateTimejava.time.Offset

1.02.32.32.32.32.3

Page 216: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

java.time.OffsetDateTimejava.time.ZonedDateTime

2.3

DoubleConverter

javax.faces.Double

n/a

double/java.lang.Double

1.0

EnumConverter

javax.faces.Enum

n/a

enum/java.lang.Enum

1.0

FloatConverter

javax.faces.Float

n/a

float/java.lang.Float

1.0

IntegerConverter

javax.faces.Integer

n/a

int/java.lang.Integer

1.0

LongConverter

javax.faces.Long

n/a

long/java.lang.Long

1.0

NumberConverter

javax.faces.Number

<f:convertNumber>

java.lang.Number

1.0

ShortConverter

javax.faces.Short

n/a

short/java.lang.Short

1.0

Page 217: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

The“ConverterID”columnbasicallyspecifiestheconverteridentifierasyoucouldspecifyintheconverterattributeofanyValueHoldercomponent,ortheconverterIdattributeofanynested<f:converter>taginordertoactivatethespecificconverter.AllUIOutputandUIInputcomponentsimplementtheValueHolderinterface.Theconverterswhichsay“n/a”inthe“Convertertag”columnareimplicitconverters.Inotherwords,youcanjustbindanybeanpropertyoftypeBigDecimal,BigInteger,boolean/Boolean,byte/Byte,char/Character,double/Double,enum/Enum,float/Float,int/Integer,long/Long,andshort/ShorttothevalueattributeofanyValueHoldercomponentandhaveJSFtoautomaticallyconvertitwithoutanyadditionalconfiguration.Only<f:convertDateTime>and<f:convertNumber>requireexplicitregistration,becausethedesiredconversionalgorithmisn’tnecessarilyobviousfromthemodelvaluealone.

InallValueHoldercomponents,theconverterwillbeinvokedduringtherenderresponsephase(sixthphase),convertingthenon-String-basedmodelvaluetoaStringsuitableforembeddinginHTML.AndinEditableValueHoldercomponents,theconverterwillalsobeinvokedduringtheprocessvalidationsphase(thirdphase),convertingthesubmittedStringrequestparametertothenon-String-basedmodelvalue.TheEditableValueHolderinterfaceextendstheValueHolderinterfaceandisimplementedbyallUIInputcomponents.

However,thisimplicitconversiondoesn’tworkonbean

Page 218: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

propertieswherethosetypesareparameterized.ImaginethatyouhaveaList<Integer>inthemodelandyou’dliketobeabletoedititasfollows:<ui:repeatvalue="#{bean.integers}"varStatus="loop">

<h:inputTextvalue="#{bean.integers[loop.index]}"/>

</ui:repeat>

Then,aftersubmitting,youwouldendupwithunconvertedStringvaluesinthelistandgetbaffledbyclasscastexceptionswhenattemptingtoiterateoverthelist.ThereasonisthattheEL(ExpressionLanguage)API(applicationprogramminginterface),whichisresponsibleforprocessingthose#{...}thingsthatare,behindthescenes,representedbyjavax.el.ValueExpressioninstances,isinitscurrentversionnotcapableofdetectingtheparameterizedtypeofagenericcollectionandjustreturnsObject.classonValueExpression#getType().JSFcan’tdomuchaboutthatlimitationofEL.Allyoucandoisexplicitlyspecifythedesiredconverterontheinputcomponent.

<ui:repeatvalue="#{bean.integers}"varStatus="loop">

<h:inputTextvalue="#{bean.integers[loop.index]}"

converter="javax.faces.Integer">

</h:inputText>

</ui:repeat>

AnalternativeistoreplacetheList<Integer>byInteger[]orevenint[].ELwillthenbeabletorecognizethevalueexpressionasanintegertypeandhenceJSFwillbeabletolocatethedesiredconverterforit.However,plainarraysinsteadofcollectionsinthemodelarea“no-no”thesedays.

Page 219: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Comingbacktotheexplicitstandardconverters<f:convertNumber>and<f:convertDateTime>,thosecanalsobenestedinanyValueHoldercomponent.Thedifferencebetween<f:convertNumber>andtheimplicitnumber-basedconvertersisthatthetagsallowmorefine-grainedsettingofconversionoptions,suchasthenumbertypeorpattern,theamountofintegerand/orfractiondigits,whethergroupingisused,andthelocale.

<F:CONVERTNUMBER><f:convertNumber> usesunderthehoodjava.text.NumberFormat. Thetypeattributespecifieswhichinstancewillbeobtainedanddefaultstonumber.Otherallowablevaluesarecurrencyandpercent.Inotherwords,thefollowingtags,

<f:convertNumbertype="number"/>

<f:convertNumbertype="currency"/>

<f:convertNumbertype="percent"/>

willunderthehoodobtaintheNumberFormatinstanceasfollows:

NumberFormatnumberFormat=

NumberFormat.getNumberInstance(locale);

NumberFormatcurrencyFormat=

NumberFormat.getCurrencyInstance(locale);

NumberFormatpercentFormat=

NumberFormat.getPercentInstance(locale);

wherethelocaleargumentcanbespecifiedbythelocaleattributeofthe<f:convertNumber>taganddefaultsto

2

3

Page 220: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

UIViewRoot#getLocale()whichinturncanbespecifiedbythelocaleattributeof<f:view>.Inotherwords,thoseinstanceswillautomaticallyapplythestandardnumberformatpatternbasedonthenumbertypeandthespecifiedlocale.Thefollowingexample,

<f:viewlocale="pt_BR">

...

<h:outputTextvalue="#{product.price}">

<f:convertNumbertype="currency"locale="en_US"/>

</h:outputText>

</f:view>

willnotformattheprice(aBigDecimalproperty)asR$12,34(Brazilianreal),butinsteadas$12.34(USdollar).Notethatthelocaleattributeofthe<f:convertNumber>tagdoesnotnecessarilyneedtobespecifiedassupportedlocaleinfaces-config.xml.Alsonotedshouldbethatthevalueattributedoesn’tnecessarilyneedtoreferaBigDecimal;anyotherjava.lang.Numbertypeisalsosupported,butforpriceswe’dofcourseliketostorethevalueinaBigDecimalinsteadof,forexample,aDoubleorFloattoavoidarithmeticerrorsduetothefloatingnatureoffloatingpointnumbers.

Incaseyouneedtochangethestandardnumberformatpatternforsomereason—forexample,becauseyou’reworkingonabankingapplicationwhichstoresfinancialdatawithfivefractions—andyou’dliketopresentthefullvalueinsomeback-endadminscreensothathumanscanifnecessaryverifythem,thenyoucanusethepatternattributeofthe

4

Page 221: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:convertNumber>tagtooverridethestandardnumberformatpatternconformtherulesofjava.text.DecimalFormat.

<f:convertNumberpattern="¤#,##0.00000"locale="pt_BR"/>

Notethatwhenthepatternattributeisspecified,thetypeattributeisignored.The“currencysign”patterncharacter“¤”specifieswheretheactualcurrencysymbolmustbeinserted.Theactualcurrencysymboldependsonthespecifiedlocale.The“comma”patterncharacter“,”specifieswhenthegroupingseparatormustbeinserted,whichisrelativetothedecimalseparatorortheendofthevalue.TheactualinsertedgroupingseparatorsymboliscoincidentallyalsoacommainUSdollarformatbutisaperiodinBrazilianrealformat.The“period”patterncharacter“.”specifiesthelocationofthedecimalseparator.TheactualinserteddecimalseparatorsymboliscoincidentallyalsoaperiodinUSdollarformatbutisacommainBrazilianrealformat.The“optionaldigit”patterncharacter“#”isinthispatternmerelyusedtoindicatewhenthegroupingseparatorsymbolshouldbeinsertedandwon’tshowanythingwhentheactualdigitisabsent.The“requireddigit”patterncharacter“0”specifiestheminimumformatwhichwillshowzerowhentheactualdigitisabsent.Followingisanexercisecodewhichshouldgiveinsightintohow<f:convertNumber>worksunderthehood:Localelocale=newLocale("pt","BR");

DecimalFormatSymbolssymbols=new

DecimalFormatSymbols(locale);

5

Page 222: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

System.out.println("Currencysymbol:"+

symbols.getCurrencySymbol());

System.out.println("Groupingsymbol:"+

symbols.getGroupingSeparator());

System.out.println("Decimalsymbol:"+

symbols.getDecimalSeparator());

DecimalFormatformatter=newDecimalFormat("¤

#,##0.00000",symbols);

System.out.println(formatter.format(new

BigDecimal("12.34")));

System.out.println(formatter.format(new

BigDecimal(".1234")));

System.out.println(formatter.format(new

BigDecimal("1234")));

System.out.println(formatter.format(new

BigDecimal("1234567.1234567")));

Theoutputshouldlookasfollows:

Currencysymbol:R$

Groupingsymbol:.

Decimalsymbol:,

R$12,34000

R$0,12340

R$1.234,00000

R$1.234.567,12346

<f:convertNumber>willalsorenderexactlythosevalues.Apartfromthepatternattribute,youcanalsofine-grainthetypeattributewithadditionalattributessuchas

Page 223: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

currencySymbol,integerOnly,groupingUsed,minIntegerDigits,maxIntegerDigits,minFractionDigits,andmaxFractionDigits.Youcanbasicallyachievethesameformattingpattern“¤#,##0.00000”asfollows:<f:convertNumbertype="currency"locale="pt_BR"

minFractionDigits="5"maxFractionDigits="5"/>

Thisisactuallymorereadableandmoreconvenientincaseyouhaveahardtimegettingoutthecurrencysignplaceholderfromyourkeyboard.Thepatternattributeisrarelymoreusefulthanfine-grainingthetypeattributewithadditionalattributes.

Incaseyou’reusing<f:convertNumber>inaUIInputcomponentandthusrequiretheendusertoenterthevalue,youshouldkeepinmindthatcurrencyandpercenttypesexplicitlyrequiretheendusertoenterthecurrencyorpercentsymbolaswell.Forthecurrencyinput,youcaneasilydisablethisbyspecifyinganemptystringasacurrencysymbolsothatyoucanputitoutsidetheinputcomponent.

<spanclass="currency">

<spanclass="symbol">$</span>

<h:inputText...>

<f:convertNumbertype="currency"currencySymbol=""/>

</h:inputText>

</span>

Forthepercenttypethisis,unfortunately,notpossible.

<F:CONVERTDATETIME><f:convertDateTime> usesunderthehood6

7

Page 224: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

java.text.DateFormat, and,sinceJSF2.3,alsojava.time.formatter.DateTimeFormatter. Inotherwords,youcanusebasicallyanykindofdateforthis.Also,thistaghasatypeattributewhichmustactuallycorrespondtotheactualtypeofthemodelvalue.Historically,itwasnotpossibletoprogrammaticallydetectthedesiredtypebasedonajava.util.Dateinstance.Thishaschangedsincethenewjava.timeAPIwhichoffersdistinctclassesforeachdatetimetype.However,inordertobeabletoreusetheexisting<f:convertDateTime>APIforthenewjava.timeAPI,newtypeshadtobeadded.Table5-2providesanoverview.

Table5-2 <f:convertDateTimetype>SupportedValues

Tagattribute

Valuetype

Actualformatter

Since

date(default)

java.util.Date(withzerotime)

DateFormat#getDateInstance()

1.0

time

java.util.Date(withzerodate)

DateFormat#getTimeInstance()

1.0

both

java.util.Date

DateFormat#getDateTimeInstance()

1.0

localDate

java.time.LocalDate

DateTimeFormatter#ofLocalizedDate()

2.3

7

8

Page 225: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ate()

localTime

java.time.LocalTime

DateTimeFormatter#ofLocalizedTime()

2.3

localDateTime

java.time.LocalDateTime

DateTimeFormatter#ofLocalizedDateTime()

2.3

offsetTime

java.time.OffsetTime

DateTimeFormatter#ISO_OFFSET_TIME

2.3

offsetDateTime

java.time.OffsetDateTime

DateTimeFormatter#ISO_OFFSET_DATE_TIME

2.3

zonedDateTime

java.time.ZonedDateTime

DateTimeFormatter#ISO_ZONED_DATE_TIME

2.3

Alongwiththetypeattribute,youshouldpreferablyalsospecifythepatternattribute,particularlywhenrequestingtheendusertoenterajava.util.Dateorjava.time.LocalXxxvalueviaaUIInputcomponent,becausetheactualpatternmayvaryinanotsoself-documentingwayacrossvariouslocales.java.time.OffSetXxxandZonedDateTimedon’thavethatproblembecausetheydefaulttotheuniversalISO8601format.

Thepatternattributeof<f:convertDateTime>

9

Page 226: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

follows,forjava.util.Date,thesamerulesasspecifiedinthejava.text.SimpleDateFormatJavadoc, andforthejava.timeAPI,thesamerulesasspecifiedinjava.time.format.DateTimeFormatterJavadoc.Theyareforthemostpartthesame,butthejava.timeformatsupportsmorepatterns.ForbothAPIs,the“dayofmonth”patterncharacteris“d”,the“monthofyear”patterncharacteris“M”,the“year”patterncharacteris“y”,the“24hhour”patterncharacteris“H”,the“minute”patternis“m”,andthe“second”patternis“s”.TheISO8601dateformatis“yyyy-MM-dd”andtheISO8601timeformatis“HH:mm:ss”.Theoffsetandzonedtimesrequireanadditionaloffsetafterthetimepart,whichisrepresentedbytheISO8601timezonepatterncharacter“X”.Examplesofvalidvaluesare“+01:00”forCET(CentralEuropeanTime),“-03:00”forBRT(BrasiliaTime),and“+5:30”forIST(IndianStandardTime).Asbefore,theoffsetandzoneddateandtimeneedtobeseparatedbythe“T”characterinsteadofaspace.Followingisanoverviewofallpossible<f:convertDateTime>typeswherebythelocalizedoneshaveanexplicitlyspecifiedpattern:<h:formid="form"><h:inputTextid="date"value="#{bean.date}">

<f:convertDateTimetype="date"pattern="yyyy-MM-

dd"/>

</h:inputText>

<h:inputTextid="time"value="#{bean.time}">

<f:convertDateTimetype="time"

pattern="HH:mm:ss"/>

</h:inputText>

<h:inputTextid="both"value="#{bean.both}">

10

11

Page 227: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:convertDateTimetype="both"pattern="yyyy-MM-

ddHH:mm:ss"/>

</h:inputText>

<h:inputTextid="localDate"value="#

{bean.localDate}">

<f:convertDateTimetype="localDate"

pattern="yyyy-MM-dd"/>

</h:inputText>

<h:inputTextid="localTime"value="#

{bean.localTime}">

<f:convertDateTimetype="localTime"

pattern="HH:mm:ss"/>

</h:inputText>

<h:inputTextid="localDateTime"value="#

{bean.localDateTime}">

<f:convertDateTimetype="localDateTime"

pattern="yyyy-MM-ddHH:mm:ss">

</f:convertDateTime>

</h:inputText>

<h:inputTextid="offsetTime"value="#

{bean.offsetTime}">

<f:convertDateTimetype="offsetTime"/>

</h:inputText>

<h:inputTextid="offsetDateTime"value="#

{bean.offsetDateTime}">

<f:convertDateTimetype="offsetDateTime"/>

</h:inputText>

<h:inputTextid="zonedDateTime"value="#

{bean.zonedDateTime}">

<f:convertDateTimetype="zonedDateTime"/>

</h:inputText>

Page 228: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:commandButtonvalue="submit"action="#

{bean.submit}"/>

<h:messagesshowSummary="false"showDetail="true"/>

</h:form>

Notethat<h:messages>isherereconfiguredtoshowthedetailinsteadofjustthesummary,becausethedetailmessageofadatetimeconversionerrorincludesinstandardJSFanexamplevaluewhichismoreusefulfortheenduserinordertounderstandtherequiredformat.Followingiswhattheassociatedbackingbeanlookslike:

@Named@RequestScoped

publicclassBean{

privateDatedate;

privateDatetime;

privateDateboth;

privateLocalDatelocalDate;

privateLocalTimelocalTime;

privateLocalDateTimelocalDateTime;

privateOffsetTimeoffsetTime;

privateOffsetDateTimeoffsetDateTime;

privateZonedDateTimezonedDateTime;

publicvoidsubmit(){

System.out.println("date:"+date);

System.out.println("time:"+time);

System.out.println("both:"+both);

System.out.println("localDate:"+localDate);

System.out.println("localTime:"+localTime);

System.out.println("localDateTime:"+

localDateTime);

System.out.println("offsetTime:"+offsetTime);

System.out.println("offsetDateTime:"+

offsetDateTime);

System.out.println("zonedDateTime:"+

Page 229: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

zonedDateTime);

}

//Add/generategettersandsetters.

}

NowthatHTML5hasbeenoutforsometimeandmoreandmorebrowserssupportthenewHTML5dateandtimeinputs, you’dbetteractivateitbydefault,becauseitcomeswithaveryusefulbuilt-indatepicker.Thewebbrowsermayshowthedatepatterninthedatepickerinalocalizedformat,butitwillalwayssubmitthevalueinISO8601format.Thisisthusveryuseful.TheHTML5dateandtimeinputscanbeactivatedbysettingthetypeattributeoftheinputtextfieldto“date”,“time”, or“datetime-local” (andthusnot“datetime”becauseithasbeendropped).WiththeJSF<h:inputText>,you’dneedtosetitasapass-throughattribute.Followingaresomeexamples:

<h:formid="form">

<h:inputTextid="localDate"a:type="date"value="#

{bean.localDate}">

<f:convertDateTimetype="localDate"pattern="yyyy-MM-

dd"/>

</h:inputText>

<h:inputTextid="localTime"a:type="time"value="#

{bean.localTime}">

<f:convertDateTimetype="localTime"pattern="HH:mm"

/>

</h:inputText>

<h:inputTextid="localDateTime"a:type="datetime-local"

value="#{bean.localDateTime}">

<f:convertDateTimetype="localDateTime"

pattern="yyyy-MM-dd'T'HH:mm">

</f:convertDateTime>

</h:inputText>

12

13

14 15

Page 230: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:commandButtonvalue="submit"action="#{bean.submit}"

/>

<h:messagesshowSummary="false"showDetail="true"/>

</h:form>

Followingishowthey’rerenderedinChromebrowser(withnewlinesadded):

StandardValidatorsWhenthesubmittedvalueissuccessfullyconvertedduringtheprocessvalidationsphase(thirdphase),thenJSFwillimmediatelyadvancetoperformvalidationontheconvertedvalue.JSFalreadyprovidesahandfulofstandardvalidatorsoutofthebox.Theyareallavailableinthejavax.faces.validatorpackage andtheyallimplementtheValidator<T>interface.Table5-3providesanoverviewofthem.

Table5-3 StandardValidatorsProvidedbyJSF

Validatorclass

ValidatorID

Validatortag

Valuetype

Since

16

Page 231: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

LongRangeValidator

javax.faces.LongRange

<f:validateLongRange>

java.lang.Number

1.0

DoubleRangeValidator

javax.faces.DoubleRange

<f:validateDoubleRange>

java.lang.Number

1.0

LengthValidator

javax.faces.Length

<f:validateLength>

java.lang.Object

1.0

RegexValidator

javax.faces.RegularExpression

<f:validateRegex>

java.lang.String

2.0

RequiredValidator

javax.faces.Required

<f:validateRequired>

java.lang.Object

2.0

BeanValidator

javax.faces.Bean

<f:validateBean>

java.lang.Object

2.0

n/a

n/a

<f:validateWholeBean>

java.lang.Object

2.3

The“ValidatorID”columnbasicallyspecifiesthevalidatoridentifierasyoucouldspecifyinthevalidatorattributeofanyEditableValueHoldercomponent,orthevalidatorIdattributeofanynested<f:validator>

Page 232: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

taginordertoactivatethespecificvalidator.Contrarytotheconverter,asingleEditableValueHoldercomponentcanhavemultiplevalidatorsattached.Theywillallbeexecutedregardlessofeachother’soutcome.

<F:VALIDATELONGRANGE>/<F:VALIDATEDOUBLERANGE>Thesevalidatorsallowyoutospecifyaminimumand/ormaximumallowednumbervalueforaninputcomponenttiedtoajava.lang.Number-basedproperty.Thosecanbespecifiedwiththeminimumandmaximumattributes.

<h:inputTextvalue="#{bean.quantity}">

<f:validateLongRangeminimum="1"maximum="10"/>

</h:inputText>

Thisis,viapass-throughattributes,alsocombinablewiththeHTML5inputtypes“number”(spinner)and“range”(slider),whichinturnrequiremin,max,andoptionallystepaspass-throughattributes.Inthisexample,#{bean.quantity}isjustanIntegerand#{bean.volume}isaBigDecimal.

<h:inputTextvalue="#{bean.quantity}"

a:type="number"a:min="1"a:max="10">

<f:validateLongRangeminimum="1"maximum="10"/>

</h:inputText>

<h:inputTextvalue="#{bean.volume}"

a:type="range"a:min="1"a:max="10"a:step="0.1">

<f:validateLongRangeminimum="1"maximum="10"/>

</h:inputText>

Page 233: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Donotethatyoucanjustuse<f:validateLongRange>onaBigDecimalproperty.Itdoesn’tcareabouttheactualjava.lang.NumbertypeofthepropertybeingaLongornot,butonlythespecifiedminimumandmaximumattributesbeingaLong.Incaseyouwanttospecifyafractional-basednumberasminimumand/ormaximum,thenuse<f:validateDoubleRange>instead.

<h:inputTextvalue="#{bean.volume}"

a:type="range"a:min="0.1"a:max="10.0"a:step="0.1">

<f:validateDoubleRangeminimum="0.1"maximum="10.0"/>

</h:inputText>

<F:VALIDATELENGTH>/<F:VALIDATEREGEX>Thesevalidatorsareprimarilydesignedforjava.lang.String-basedproperties.<f:validateLength>willfirstconvertthesubmittedvaluetostringbycallingObject#toString()onitandthenvalidatetheString#length()resultbasedonthespecifiedminimumand/ormaximumattributes.<f:validateRegex>willcastthesubmittedvaluetoStringandthencheckifString#matches()returnstrueforthespecifiedpatternattribute.Inotherwords,itdoesn’tacceptanyotherpropertytypethanjava.lang.String.Imaginethatyouwanttovalidateavaluetobealwaysthreedigits;thustherearethreepossibleways:<h:inputTextvalue="#{bean.someStringOrInteger}"maxlength="3">

<f:validateLengthminimum="3"maximum="3"/>

Page 234: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</h:inputText>

<h:inputTextvalue="#{bean.someString}"maxlength="3">

<f:validateRegexpattern="[0-9]{3}"/>

</h:inputText>

<h:inputTextvalue="#{bean.someInteger}"maxlength="3">

<f:validateLongRangeminimum="100"maximum="999"/>

</h:inputText>

Themaxlength="3"attributeisjusttheresothattheendusercan’tentermorethanthreecharactersontheclientsideanyway.Storingnumbersasstringsisplainnonsense,sothesecondwayisscratched.Thatleavesuswiththefirstorthirdway.Technicallyitdoesnotreallymatterwhichoneyoupick.Thefirstwayisarguablymoreself-documentingbecauseyouactuallywanttovalidatethelength,nottherange.

Comingbackto<f:validateRegex>,thepatternattributefollowsexactlythesameregularexpressionrulesasspecifiedinjava.util.regex.Pattern. However,there’sonepotentialcaveat:thenecessaryamountofescapebackslashesdependsonthecurrentlyusedELimplementation.InOracle’sELimplementation(com.sun.el.*),youneedtwobackslashes,exactlyasinaregularJavaString,butinApache’sELimplementation(org.apache.el.*),youmustuseonebackslash,otherwiseitwillerroroutoritwon’tmatchasyou’dexpect.Asofnow,Payara,WildFly,Liberty,andWebLogicuseOracle’sELimplementation,andTomEEandTomcatuseApache’sELimplementation.Inotherwords,thefollowingexamplewillworkonserversusingOracleELbutwon’tworkonserversusingApacheEL.

17

Page 235: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:inputTextvalue="#{bean.someString}"maxlength="3">

<f:validateRegexpattern="\\d{3}"/>

</h:inputText>

WhenusingApacheEL,youneedpattern="\d{3}"instead.Ontheotherhand,theregularexpressionpattern\dactuallymeans“anydigit”andthusmatchesnotonlytheLatindigitsbutalsotheHebrew,Cyrillic,Arabic,Chinese,etc.Ifthatwasnotyourintent,you’dbetterusethe[0-9]pattern.

<F:VALIDATEREQUIRED>Thisisaslightlyoddbeast.Thatis,allUIInputcomponentsalreadyhavearequiredattributeofferingexactlythedesiredfunctionality.Whywouldyouthenuseawhole<f:validateRequired>taginstead?ItwasaddedinJSF2.0specificallyfor“CompositeComponents”(moreonthislater,inChapter7).Moretothepoint,insomecompositecomponentcompositionsthetemplateclientisgiventheopportunitytoattachconvertersandvalidatorstoaspecificEditableValueHolderinterfaceexposedbythecompositecomponent,whichinturnreferencesoneormoreUIInputcomponentsenclosedinthecompositecomponentimplementation.Followingisanexampleofsuchacompositecomponent:<cc:interface>...

<cc:editableValueHolder

name="inputs"targets="input1input3">

</cc:editableValueHolder>

</cc:interface>

<cc:implementation>

Page 236: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

...

<h:inputTextid="input1".../>

<h:inputTextid="input2".../>

<h:inputTextid="input3".../>

...

</cc:implementation>

Andfollowingisanexampleofthetemplateclient:

<my:compositeComponent...>

<f:validateRequiredfor="inputs"/>

</my:compositeComponent>

Asyoumighthaveguessed,theforattributemustexactlymatchthenameattributeofexposed<cc:editableValueHolder>andthisvalidatorwillbasicallytargettheenclosedinputcomponentsidentifiedbyinput1andinput3(andthusnotinput2)andthuseffectivelymakethemrequired="true".Thisforattributeis,bytheway,alsopresentonallotherconverterandvalidatortags.

<F:VALIDATEBEAN>/<F:VALIDATEWHOLEBEAN>Whenused,thesetagshavearequireddependencyontheBeanValidationAPI(applicationprogramminginterface),previouslymorecommonlyknownas“JSR303.”LikeJSF,BeanValidationispartoftheJavaEEAPI,alreadyincludedinanyJavaEEapplicationserver.InTomcatandotherservletcontainers,you’dneedtoinstallitseparately.InJavacode,BeanValidationisrepresentedbyannotationsandinterfaces

Page 237: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ofthejavax.validation.*package,suchas@NotNull,@Size,@Pattern,ConstraintValidator,etc.CurrentlythemostpopularimplementationisHibernateValidator.

JSFautomaticallydetectsthepresenceofBeanValidationandwillinsuchacasetransparentlyprocessallBeanValidationconstraintsduringtheendoftheprocessvalidationsphase(thirdphase),regardlessoftheoutcomeofJSF’sownvalidators.Ifdesired,thiscanbedisabledapplication-widewiththefollowingcontextparameterinweb.xml:

<context-param>

<param-name>

javax.faces.validator.DISABLE_DEFAULT_BEAN_VALIDATOR

</param-name>

<param-value>true</param-value>

</context-param>

Or,ifthisisalittletoorough,youcanfine-grainitwithhelpofthe<f:validateBean>tagwrappingagroupofUIInputcomponents,ornestedinthem.Whenthedisabledattributeofthe<f:validateBean>tagissettotrue,thenanyBeanValidationwillbedisabledonthetargetUIInputcomponents.ThefollowingcodewilldisableanyBeanValidationonlyontheparentUIInputcomponent.

<h:inputText...>

<f:validateBeandisabled="true"/>

</h:inputText>

AndthefollowingcodewilldisableanyBeanValidationonly

18

Page 238: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

onUIInputcomponentsidentifiedbyinput3,input4,andinput5:<h:inputTextid="input1".../><h:inputTextid="input2".../>

<f:validateBeandisabled="true">

<h:inputTextid="input3".../>

<h:inputTextid="input4".../>

<h:inputTextid="input5".../>

<f:validateBean>

ItisimportanttokeepinmindisthatthiswillonlydisableJSF-managedBeanValidationandthusnot,forexample,JPA-managedBeanValidation.So,ifyouhappentouseJPA(JavaPersistenceAPI)topersistyourentitieswhicharefilledoutbyJSFcomponentswithBeanValidationdisabled,thenJPAwouldstillperformBeanValidationonitsbehalf,fullyindependentlyfromJSF.IncaseyouwanttodisableBeanValidationontheJPAsideaswell,youneedtosetthepropertyjavax.persistence.validation.modetoNONEinpersistence.xml(seealsothejavax.persistence.ValidationModeJavadoc).

<property

name="javax.persistence.validation.mode">NONE</property>

WiththevalidationGroupsattributeofthe<f:validateBean>tagyoucanifnecessarydeclareoneormorevalidationgroups.Insuchacase,onlytheBeanValidationconstraintswhichareregisteredonthesamegroupwillbeprocessed.Imaginethefollowingmodel:@NotNullprivateStringvalue1;

19

Page 239: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@NotNull(groups=NotNull.class)

privateStringvalue2;

@NotNull(groups={NotNull.class,Default.class})

privateStringvalue3;

NotethatthegroupsattributeofanyBeanValidationconstraintmustreferenceaninterface,butitmaybeanyoneyouwant.Forsimplicity,intheaboveexamplewe’rejustreusingthejavax.validation.constraints.NotNull

interfaceasagroupidentifier.Thecommonpracticeis,however,tocreateyourownmarkerinterfaceforthedesiredgroup.

Alsonotunimportantisthatthe@NotNullwouldonlyworkwhenyou’veconfiguredJSFtointerpretemptystringsubmittedvaluesasnull;otherwiseitwouldpollutethemodelwithemptystringsinsteadofnullsandcausethe@NotNullnottobeabletodoitsjobbecauseanemptystringisnotnull.Asareminder,theweb.xmlcontextparameterofinterestisasfollows:

<context-param>

<param-name>

javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_A

S_NULL

</param-name>

<param-value>true</param-value>

</context-param>

Now,whensubmittinganemptyformwhilehavingthosemodelpropertiesreferencedinthefollowinginput

Page 240: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

components,withoutany<f:validateBean>onthem:<h:inputTextvalue="#{bean.value1}"/>

<h:inputTextvalue="#{bean.value2}"/>

<h:inputTextvalue="#{bean.value3}"/>

youwillreceiveavalidationerroronBeanValidationconstraintsbelongingtothejavax.validation.groups.Defaultgroup,whicharethusthegrouplessvalue1andtheexplicitlygroupedvalue3.Thevalue2won’tbeBeanValidatedasitdoesn’thavethedefaultgroupexplicitlydeclared.

And,whensubmittinganemptyformwhilehaving<f:validateBean>withvalidationGroupssettoNotNull.class:<f:validateBeanvalidationGroups="javax.validation.constraints.NotNull">

<h:inputTextvalue="#{bean.value1}"/>

<h:inputTextvalue="#{bean.value2}"/>

<h:inputTextvalue="#{bean.value3}"/>

</f:validateBean>

youwillreceiveavalidationerroronBeanValidationconstraintsbelongingtothejavax.validation.constraints.NotNullgroup,whicharethusthevalue2andvalue3,whichexplicitlyhavethisgroupdeclared.Thegrouplessvalue1won’tbeBeanValidatedasitonlyimpliesthedefaultgroup.

Finally,whensubmittinganemptyformwhilehavinga<f:validateBean>withbothgroupsspecifiedinvalidationGroupsattributeasacommaseparatedstring:

<f:validateBean

Page 241: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

validationGroups="javax.validation.groups.Default,

javax.validation.constraint

s.NotNull">

<h:inputTextvalue="#{bean.value1}"/>

<h:inputTextvalue="#{bean.value2}"/>

<h:inputTextvalue="#{bean.value3}"/>

</f:validateBean>

youwillreceiveavalidationerroronallinputs,becausetheyallmatchatleastoneofthespecifiedgroups.Inreal-worldapplications,however,thisgroupingfeaturehasverylittleuse.It’sonlyreallyusefulwhenthegroupedfieldscanbevalidatedatthesametimebythesamevalidator.WithBeanValidation,theonlywaytoachievethatistoputacustomConstraintannotationonthebeanclassitself,getaninstanceofthatbeanwiththevaluespopulated,andthenpassittothecustomConstraintValidatorassociatedwiththecustomConstraintannotation.Imaginea“period”entityhavinga“startdate”propertywhichshouldalwaysbebeforethe“enddate”property.Itwouldlooksomethinglikethefollowing:@PeriodConstraint

publicclassPeriodimplementsSerializable{

@NotNull

privateLocalDatestartDate;

@NotNull

privateLocalDateendDate;

//Add/generategettersandsetters.

}

Withthefollowingcustomconstraintannotation:

Page 242: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Constraint(validatedBy=PeriodValidator.class)

@Target(TYPE)

@Retention(RUNTIME)

public@interfacePeriodConstraint{

Stringmessage()default"Startdatemustbebeforeend

date";

Class<?>[]groups()default{};

Class<?>[]payload()default{};

}

Andthefollowingcustomconstraintvalidator:

publicclassPeriodValidator

implementsConstraintValidator<PeriodConstraint,Period>

{

@Override

publicbooleanisValid

(Periodperiod,ConstraintValidatorContextcontext)

{

return

period.getStartDate().isBefore(period.getEndDate());

}

}

Yousee,BeanValidationexpectsthatthemodelvaluesarepresentwhenperformingthevalidation.IntheJSFperspective,thismeansthatthemodelvaluesmustbeupdatedbeforeprocessingthevalidations.However,thisdoesn’tfitintotheJSFlifecyclewhereinthemodelvaluesareonlyupdatedafterthevalidationsaresuccessfullyprocessed.Essentially,JSFwouldneedtoclonethebeaninstance,populateitwiththedesiredmodelvalues,invokeBeanValidationonit,collectanyvalidationerrors,andthendiscardtheclonedbeaninstance.

Thisisexactlywhatthe<f:validateWholeBean>

Page 243: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

tag,introducedwithJSF2.3,isdoingunderthehood.Followingisanexampleformwhereinthiscodeisbeingused:<h:form>

<h:inputTexta:type="date"value="#

{booking.period.startDate}">

<f:convertDateTimetype="localDate"

pattern="yyyy-MM-dd"/>

</h:inputText>

<h:inputTexta:type="date"value="#

{booking.period.endDate}">

<f:convertDateTimetype="localDate"

pattern="yyyy-MM-dd"/>

</h:inputText>

<h:commandButtonvalue="Submit"/>

<h:messages/>

<f:validateWholeBeanvalue="#{booking.period}"/>

</h:form>

Withthisbackingbean:

@Named@ViewScoped

publicclassBookingimplementsSerializable{

privatePeriodperiod=newPeriod();

//Add/generategetter.

}

Donotethat<f:validateWholeBean>isexplicitlyplacedasthelastchildoftheparent<h:form>,whichensuresthatthevalidationisperformedasthelastthingafterallindividualinputcomponentsinthesameform.Thisisasperthespecification;theJSFimplementationmaythrowa

Page 244: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

runtimeexceptionwhenthetagismisplaced.

ImmediateAttributeTheEditableValueHolder,ActionSource,andAjaxBehaviorinterfacesalsospecifyanimmediatepropertywhichbasicallymapstotheimmediateattributeofallUIInputandUICommandcomponentsandthe<f:ajax>tag.WhensettotrueonanEditableValueHoldercomponent,thenanythingthatnormallytakesplaceduringtheprocessvalidationsphase(thirdphase)aswellastheupdatemodelvaluesphase(fourthphase)willbeperformedduringtheapplyrequestvaluesphase(secondphase).Whenconversionorvalidationfailsonthem,thenthelifecyclewillalsoskiptheprocessvalidationsphase(thirdphase).WhensettotrueonanActionSourcecomponentorAjaxBehaviortag,thenanythingthatnormallytakesplaceduringtheinvokeapplicationphase(fifthphase)willbeperformedduringtheapplyrequestvaluesphase(secondphase)andthenonlyifconversionandvalidationhaven’tfailed.

Historically,thisattributewasmainlyusedtobeabletoperforman“inner”actionontheform,usuallytoloadachildinputcomponentdependingonthesubmittedvalueoftheparentinputcomponent,withoutbeingblockedbyconversionorvalidationerrorscomingfromotherinputcomponentsinthesameform.Acommonusecasewaspopulatingachilddrop-downonthechangeofaparentdrop-down.

<h:selectOneMenuvalue="#{bean.country}"required="true"

immediate="true"

Page 245: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

onchange="submit()"valueChangeListener="#

{bean.loadCities}">

<f:selectItemsvalue="#{bean.countries}"/>

</h:selectOneMenu>

<h:selectOneMenuvalue="#{bean.city}"required="true">

<f:selectItemsvalue="#{bean.cities}"/>

</h:selectOneMenu>

ThisapproachobviouslypredatestheWeb2.0erawhereinyou’djustuseAjaxforthis.Understandthattheimmediateattributehasessentiallybecomeuselessforthispurposesincetheintroductionof<f:ajax>inJSF2.0.Exactlythesameusecasecanbeachievedinamuchcleanerwayasfollows:<h:selectOneMenuvalue="#{bean.country}"

required="true">

<f:selectItemsvalue="#{bean.countries}"/>

<f:ajaxlistener="#{bean.loadCities}"render="city"

/>

</h:selectOneMenu>

<h:selectOneMenuid="city"value="#{bean.city}"

required="true">

<f:selectItemsvalue="#{bean.cities}"/>

</h:selectOneMenu>

AsyoulearnedinChapter4,theexecuteattributeof<f:ajax>alreadydefaultsto@this,soit’sjustomitted.ThisalsomeansthatallotherEditableValueHoldercomponentsinthesameformwon’tbeprocessedandthuswon’tcause#{bean.loadCities}evertobeblockedbyconversionorvalidationerrorscomingfromotherinputs.

Thesedays,withAjaxmagicandall,theimmediateattributehasthuslostitsmainusecase.JSFcoulddoaswell

Page 246: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

withoutit.Duetoitshistoricusecase,manystartersmaymistakeitsprimarypurposetobe“skipallvalidation.”Thisis,however,nottrue.Forthat,you’dneedtofine-tunetheexecuteattributeof<f:ajax>sothatitonlycoverstheinputcomponentsthatreallyneedtobevalidated.Incaseyouwanttoactually“skipallvalidation”whilesubmittingtheentireform,you’dbestuseBeanValidationconstraintsinstead(the@NotNullandfriends)andsimplyhave<f:validateBeandisabled="true">wrappingtheentireform.

CustomConvertersFromthebeginningJSFhassupportedcustomconverters.Themainusecaseistobeabletoconvertanon-standardmodelvalue,suchasapersistenceentityspecifictothewebapplication.Thelesscommonusecaseistoextendanexistingstandardconverterandsetsomecommonlyuseddefaultsinitsconstructorsothatyoucangetawaywithlesscodeinordertodeclarethedesiredstandardconverterconfigurationintheview.

Imaginethatyouwanttobeabletocreatemaster-detailpagesonyourpersistenceentitieswhereinyou’dliketopasstheIDoftheentityaroundasarequestparameterfromthemasterpagetothedetailpage.Followingisanexampledatatableinthemasterpageproductslist.xhtmlbasedonafictiveProductentity:

<h:dataTablevalue="#{listProducts.products}"var="product">

<h:column>#{product.id}</h:column>

<h:column>#{product.name}</h:column>

<h:column>#{product.description}</h:column>

Page 247: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:column>

<h:linkvalue="Edit"outcome="edit">

<f:paramname="id"value="#{product.id}"/>

</h:link>

</h:column>

</h:dataTable>

Notethelastcolumnofthetable.Itgeneratesalinktothedetailpageproductsedit.xhtmlwherebytheIDoftheentityispassedasaGETrequestparameterasin/product.xhtml?id=42.Inthedetailpage,youcanuse<f:viewParam>tosettheGETrequestparameterinthebackingbean.

<f:metadata>

<f:viewParamname="id"value="#{editProduct.product}"

required="true"requiredMessage="Badrequest">

</f:viewParam>

</f:metadata>

...

<h:form>

<h1>Editproduct#{editProduct.product.id}</h1>

<h:inputTextvalue="#{editProduct.product.name}"/>

<h:inputTextvalue="#{editProduct.product.description}"

/>

...

</h:form>

However,there’sonesmallproblem:theGETrequestparameterisinJavaperspectivebasicallyaStringrepresentingtheproductIDwhiletheproductpropertyoftheEditProductbackingbeanactuallyexpectsawholeProductentityidentifiedbythepassed-inID.

@Named@ViewScoped

Page 248: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicclassEditProductimplementsSerializable{

privateProductproduct;

//Getter+setter.

}

Forexactlythisconversionstep,acustomconverterhastobecreatedwhichiscapableofconvertingbetweenaStringrepresentingtheproductIDandanObjectrepresentingtheProductentity.JSFoffersthejavax.faces.convert.Converterinterface togetstarted.FollowingisaconcreteexampleofsuchaProductConverter:@FacesConverter(forClass=Product.class,managed=true)

publicclassProductConverterimplements

Converter<Product>{

@Inject

privateProductServiceproductService;

@Override

publicStringgetAsString

(FacesContextcontext,UIComponentcomponent,

Productproduct)

{

if(product==null){

return"";

}

if(product.getId()!=null){

returnproduct.getId().toString();

20

Page 249: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

else{

thrownewConverterException(

newFacesMessage("InvalidproductID"),

e);

}

}

@Override

publicProductgetAsObject

(FacesContextcontext,UIComponentcomponent,

Stringid)

{

if(id==null||id.isEmpty()){

returnnull;

}

try{

return

productService.getById(Long.valueOf(id));

}

catch(NumberFormatExceptione){

thrownewConverterException(

newFacesMessage("InvalidproductID"),

e);

}

}

}

Thereareseveralimportantthingstonotehereinthe@FacesConverterannotation.First,theforClass

Page 250: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

attributebasicallyspecifiesthetargetentitytypeforwhichthisconvertershouldautomaticallyrunduringtheprocessvalidationsphase(thirdphase)andtherenderresponsephase(sixthphase).Thiswayyoudon’tneedtoexplicitlyregistertheconverterintheview.Incaseyouwantedtodoso,you’dreplacetheforClassattributebythevalueattributespecifyingtheuniqueidentifieroftheconverter,forexample:@FacesConverter(value="project.ProductConverter",

managed=true)

ThenyoucanspecifyexactlythatconverterIDintheconverterattributeofanyValueHoldercomponent,ortheconverterIdattributeofanynested<f:converter>tag.

<f:viewParamname="id"value="#{editProduct.product}"

converter="project.ProductConverter"

required="true"requiredMessage="Badrequest">

</f:viewParam>

ButthisisnotnecessarywhenyoujustkeepusingtheforClassattribute.Notethatyoucan’tspecifyboth.It’soneortheotherwherethevalueattributetakesprecedenceovertheforClass.So,ifyouspecifyboth,theforClassattributeisessentiallyignored.Wedon’twanttohavethatasit’smuchmorepowerfulforthisparticularpurposeoftransparentlyconvertingwholeentities.

Thesecondthingtonoteintheannotationisthemanagedattribute.ThisisnewsinceJSF2.3.Essentially,thismanagestheconverterinstanceintheCDIcontext.Settingthemanagedattributetotrueismandatoryinordertogetdependencyinjectiontoworkintheconverter.Previously,this

Page 251: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

wasworkedaroundbymakingtheconverteritselfamanagedbean.

IfyouhaveworkedwithJSFconvertersbefore,you’llalsonoticetheinterfacenowfinallybeingparameterized.TheinterfacepredatesJava1.5andwashencenotparameterizedfromthebeginning.WithaConverter<T>,thegetAsObject()nowreturnsaTinsteadofObjectandthegetAsString()nowtakesaTasvalueargumentinsteadofObject.Thissavesunnecessaryinstanceofchecksand/orcasts.

NotethatJSF’sownstandardconverterswhichpredateJSF2.3(currently,basicallyallofthemthus)arefrozenintimeandcannottakeadvantageofthisastheywouldotherwisenolongerbebackwardcompatible.Inotherwords,theyarestillrawtypes.Thatis,there’sasmallbutnotunavoidablechancethatsomeoneisprogrammaticallyusingJSFconvertersinplainJavacodeinsteadoflettingJSFdealwiththem.ThatplainJavacodewouldnolongercompileifthestandardconverterswereparameterized.It’sessentiallythesamereasonthattheMap#get()explicitlytakesObjectinsteadofKasargument.Furtherthere’sayetsmallerbutstillnotunavoidablechancethatsomeonehascreatedacustomconverterwhichextendsastandardconverter,butalsoexplicitlyredeclarestheinterface.Somethinglikethefollowing:publicclassExtendedNumberConverterextendsNumberConverterimplementsConverter

{

//...

}

Suchanobscureconverterwouldnolongercompileif

21

Page 252: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

NumberConverterwasparameterizedinsomeway.EvenifweparameterizeNumberConverterasaConverter<Object>,thecompilerwoulderroronExtendedNumberConverterasfollowsandhencebreakbackwardcompatibility:

TheinterfaceConvertercannotbeimplementedmorethanoncewithdifferentarguments:Converter<Object>andConverter

ComingbacktoourProductConverterimplementation,inthegetAsString()you’llnoticethattheconverterexplicitlyreturnsanemptystringwhenthemodelvalueisnull.ThisisaspertheJavadoc. ThetechnicalreasonisthatJSFwon’trendertheassociatedHTMLattributewhentheevaluatedvalueisnull.Ingeneral,thisisnotabigproblem.ThefewerunusedattributesinthegeneratedHTMLoutput,thebetteritis.Only,thiswon’tworkasexpectedforthe<option>ofa<select>element.Ifthecustomconverterwouldreturnnullinsteadofanemptystring,thenthe<option>elementwouldberenderedwithoutanyvalueattributeandthusfallbacktosubmittingitstextcontentinstead.Awkwardindeed,butthisisliterallyspecifiedintheHTMLspecification. Inotherwords,ifyouhaveaconverterthatincorrectlyreturnsnullinsteadofanemptystring,andyouhaveadrop-downlistwiththeassociatedentitiesalongwithadefaultoptionasfollows:<h:selectOneMenuvalue="#{bean.product}">

<f:selectItemitemValue="#{null}"itemLabel="Please

select..."/>

<f:selectItemsvalue="#{bean.products}"

22

23

Page 253: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

var="product"itemLabel="#{product.name}">

</f:selectItems>

</h:selectOneMenu>

thenthewebbrowserwould,duringsubmittingthedefaultoption,sendtheliteralstring“Pleaseselect…”totheserverinsteadofanemptystring.ThiswouldcauseaNumberFormatExceptioninProductConverter#getAsObject()whileweintendtoreturnnullhere.ThecorrectsolutionisthustoletthegetAsString()returnanemptystringincasethemodelvalueisnull.

IncaseyouhavemorepersistenceentitiesforwhichyouneedaJSFconverter,andwanttoavoidrepeatingessentiallythesameProductConverterlogicforallotherpersistenceentities,youcancreateagenericJSFconverterforthem.ThisworksonlyifallyourpersistenceentitiesextendfromthesamebaseclasswhereinthegetId()isdefined.

@MappedSuperClass

publicabstractclassBaseEntityimplementsSerializable{

@Id@GeneratedValue(strategy=IDENTITY)

privateLongid;

publicLonggetId(){

returnid;

}

}

Andifyouhaveabaseentityserviceforallofthem:

@Stateless

Page 254: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicclassBaseEntityService{

@PersistenceContext

privateEntityManagerentityManager;

@TransactionAttribute(SUPPORTS)

public<EextendsBaseEntity>EgetById(Class<E>type,

Longid){

returnentityManager.find(type,id);

}

}

thegenericconvertercanthenlookasfollows:

@FacesConverter(forClass=BaseEntity.class,managed=true)

publicclassBaseEntityConverterimplements

Converter<BaseEntity>{

@Inject

privateBaseEntityServicebaseEntityService;

@Override

publicStringgetAsString

(FacesContextcontext,UIComponentcomponent,

BaseEntityentity)

{

if(entity==null){

return"";

}

if(entity.getId()!=null){

returnentity.getId().toString();

}

else{

thrownewConverterException(

newFacesMessage("InvalidentityID"),e);

}

}

Page 255: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Override

publicBaseEntitygetAsObject

(FacesContextcontext,UIComponentcomponent,String

id)

{

if(id==null||id.isEmpty()){

returnnull;

}

ValueExpressionvalue=

component.getValueExpression("value");

Class<?extendsBaseEntity>type=(Class<?extends

BaseEntity>)

value.getType(context.getELContext());

try{

returnbaseEntityService.getById(type,

Long.valueOf(id));

}

catch(NumberFormatExceptione){

thrownewConverterException(

newFacesMessage("InvalidentityID"),e);

}

}

}

ThekeyhereisthustheValueExpression#getType()call.ThisreturnstheactualtypeofthepropertybehindtheELexpressionassociatedwiththecomponent’svalueattribute.Incaseof<f:viewParamvalue="#{editProduct.product}">thiswouldthusreturnProduct.class,whichfitsClass<?extendsBaseEntity>.

Comingbacktothelesscommonusecaseofacustomconverter,extendingastandardconverter,imaginethatyouhavea<f:convertDateTime>configurationwhichis

Page 256: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

repeatedeverywhereinyourwebapplication:

<f:convertDateTimetype="localDate"pattern="yyyy-MM-dd"/>

Andyou’dliketoreplaceitwithsomethinglikethefollowing:<t:convertLocalDate/>

Thenonewayistojustextendit,setthedefaultsintheconstructor,registeritinthe*.taglib.xmlfile,andthat’sit.FollowingiswhatsuchaLocalDateConvertercanlooklike:@FacesConverter("project.ConvertLocalDate")publicclassLocalDateConverterextends

DateTimeConverter{

publicLocalDateConverter(){

setType("localDate");

setPattern("yyyy-MM-dd");

}

}

Andhere’stheWEB-INFexample.taglib.xmlentry.

<tag>

<tag-name>convertLocalDate</tag-name>

<converter>

<converter-id>project.ConvertLocalDate</converter-id>

</converter>

</tag>

Alternatively,youcanalsomakeitanimplicitconverterbygettingridoftheconverterIDandmakingitaforClassconverter.

Page 257: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@FacesConverter(forClass=LocalDate.class)

Thiswayyoudon’tevenneedany<t:convertLocalDate>tag.Don’tforgettoremovethe<tag>entryinexample.taglib.xml.Theycannotbeusedsimultaneously.Ifyouneedsuchcase,forexample,becauseyouwanttoabletochangetheLocalDatepattern,createanothersubclass.

YoucanevenhaveaforClassconverterforjava.lang.Stringtypedproperties.Thisisveryusefulwhenyouwanthaveanautomaticapplication-widestring-trimmingstrategywhichshouldpreventthemodelfrombeingpollutedwithleadingortrailingwhitespaceonuser-submittedvalues.Followingiswhatsuchaconvertercanlooklike:@FacesConverter(forClass=String.class)

publicclassTrimConverterimplementsConverter<String>

{

@Override

publicStringgetAsString

(FacesContextcontext,UIComponentcomponent,

StringmodelValue)

{

returnmodelValue==null?"":modelValue;

}

@Override

publicStringgetAsObject(FacesContextcontext,

UIComponentcomponent,StringsubmittedValue)

{

Page 258: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

if(submittedValue==null||

submittedValue.isEmpty()){

returnnull;

}

Stringtrimmed=submittedValue.trim();

returntrimmed.isEmpty()?null:trimmed;

}

}

Lastbutnotleast,whenyouneedtoprovidewholeentitiesasSelectItemvaluesofaselectioncomponentasbelow(seealsoChapter4),alongwithacustomconverterforCountry.class:<h:selectOneMenuvalue="#{bean.country}">

<f:selectItemitemValue="#{null}"itemLabel="--

selectone--"/>

<f:selectItemsvalue="#{bean.availableCountries}"

var="country">

itemValue="#{country}"itemLabel="#

{country.name}"

</f:selectItems>

</h:selectOneMenu>

wheretheassociatedbackingbeanpropertiesaredeclaredasfollows:privateCountrycountry;privateList<Country>availableCountries;

thenyouneedtokeepinmindthattheentityhasitsequals()andhashCode()properlyimplemented.OtherwiseJSFmaythrowaconfusingvalidationerrorwhensubmittingtheform.

Page 259: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ValidationError:Valueisnotvalid

Thismayhappenwhenthebeanisrequestscopedinsteadofviewscopedandthusrecreatesthelistofavailablecountriesduringeverypostback.Aspartofsafeguardagainsttamperedrequests,JSFwillreiterateovertheavailableoptionsinordertovalidateiftheselectedoptionisindeedamongthem.JSFwillusetheObject#equals()methodtotesttheselectedoptionagainsteachavailableoption.Ifthishasn’treturnedtrueforanyoftheavailableoptions,thentheabove-mentionedvalidationerrorwillbethrown.

ContinuingwiththeBaseEntityexample,here’showyou’dbestimplementitsequals()andhashCode()methods.

@Override

publicbooleanequals(Objectother){

if(getId()!=null

&&getClass().isInstance(other)

&&other.getClass().isInstance(this))

{

returngetId().equals(((BaseEntity)other).getId());

}

else{

return(other==this);

}

}

@Override

publicinthashCode(){

if(getId()!=null){

returnObjects.hash(getId());

}

else{

returnsuper.hashCode();

}

Page 260: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

NotethebidirectionalClass#isInstance()testintheequals()method.ThisisdoneinsteadofgetClass()==other.getClass(),becausethatwouldreturnfalsewhenyourpersistenceframeworkusesproxies,suchasHibernate.

CustomValidatorsAlso,validatorscanbecustomizedinJSFfromthebeginning.AsalmosteveryeverybasicusecaseisalreadycoveredbystandardJSFvalidatorsandevenBeanValidationconstraints,suchaslength,range,andpatternvalidation,themostcommonusecaselefttoacustomJSFvalidatorisvalidatingthedataintegritybytestingthesubmittedvalueagainstdatabase-basedconstraints.Generally,thoseconcernuniqueconstraints.

Agoodreal-worldexampleisvalidatingduringe-mail-basedsignuporwhilechangingthee-mailaddressintheuseraccountmanagementpagewhenthespecifiede-mailaddressisnotalreadyinuse.Particularly,thechangeeventcan’tbetestedwithaBeanValidationconstraintinasimpleway,becauseBeanValidationdoesn’toffertheopportunitytocomparetheoldvaluewiththenewvaluewithoutre-obtainingtheentityfromthedatabase.Tostart,justimplementthejavax.faces.validator.Validatorinterfaceaccordingly.

@FacesValidator(value="project.UniqueEmailValidator",

managed=true)

24

Page 261: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicclassUniqueEmailValidatorimplements

Validator<String>{

@Inject

privateUserServiceuserService;

@Override

publicvoidvalidate

(FacesContextcontext,UIComponentcomponent,String

email)

throwsValidatorException

{

if(email==null||email.isEmpty()){

return;//Let@NotNullorrequired=truehandle

this.

}

StringoldEmail=(String)((UIInput)

component).getValue();

if(!email.equals(oldEmail)&&

userService.exist(email)){

thrownewValidatorException(

newFacesMessage("Emailalreadyinuse"));

}

}

}

Inordertogetittorun,justspecifyexactlythedeclaredvalidatorIDinthevalidatorattributeofanyEditableValueHoldercomponent,orthevalidatorIdattributeofanynested<f:validator>tag.

<h:inputTextvalue="#{signup.user.email}"

validator="project.UniqueEmailValidator">

</h:inputText>

Page 262: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

WhenlookingattheUniqueEmailValidatorclass,you’llnoticethattheannotationandtheinterfacealsogotthesameJSF2.3changesastheconverter.Likethe@FacesConverter,the@FacesValidatorannotation,sinceJSF2.3,alsogotanewmanagedattributewhichshouldenabledependencyinjectioninthevalidatorimplementation.And,liketheConverter<T>,theValidator<T>alsogotparameterizedwherebythevalidate()methodnowtakesaTinsteadofObjectasavalueargument.

Youalsoneedtomakesurethatyourvalidatorsareimplementedsothattheyskipvalidationwhenthevalueargumentisnullorempty.Historically,inJSF1.x,thevalidate()methodwouldalwaysbeskippedwhenthevalueargumentisnull.However,thishaschangedsincetheintegrationofBeanValidationinJSF2.0,therebybreakingbackwardcompatibilityonexistingJSF1.x-basedcustomvalidators.Thisbreakingchangecouldbeturnedoffbyexplicitlysettingthefollowingweb.xmlcontextparameter:<context-param>

<param-

name>javax.faces.VALIDATE_EMPTY_FIELDS</param-name>

<param-value>false</param-value>

</context-param>

Thedisadvantageofthisisthatthe@NotNullofBeanValidationwon’tbetriggeredbyJSFandyou’dbasicallyneedtorepeatthisconstraintforallJSFinputcomponentsbyexplicitlysettingtheirrequiredattributetotrue.You’dbetternotdothisandjustkeepperformingthenullandemptycheckinyourcustomvalidator.HavingvalidationconstraintsatasingleplaceinthemodelwithhelpofBean

Page 263: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ValidationismoreDon’tRepeatYourself(DRY)thanrepeatingthevalidationconstraintsacrossdifferentlayersusingtheverysamemodel.

Finally,theoldvaluecansimplybeobtainedfromUIInput#getValue()whichbasicallyreturnsthecurrentvalueattributeoftheUIInputcomponent.

Comingbacktotheusecaseofvalidatingtheuniquenessofthesubmittedvalue,ofcourseyoucouldalsoskipthisandinsertthedataanywayandcatchanyconstraintviolationexceptioncomingfromthepersistencelayeranddisplayafacesmessageaccordingly.However,thisdoesn’tgowellwiththecurrenttrendofimmediatefeedbackdirectlyafterchangingtheinputfieldintheuserinterface.

Inthisspecificusecaseofvalidatingauniquee-mailaddressduringsignup,however,theremaybeanotherreasonnottogiveawaytoomuchdetailabouttheuniquenessofthespecifiede-mailaddress:security.Insuchacase,you’dbestletthesignupcompleteexactlythesamewayasifitwassuccessfulwherebyyoutelltheusertocheckthemailbox,butbehindthescenesactuallysendadifferente-mailtothetargetrecipient,ratherthananactivatione-mail,preferablynotmorethanoncedaily.Thee-mailwouldbesimilartothefollowing:

Dearuser,Itlookslikeyouorsomeoneelsetriedtosignuponour

websiteusingyouremailaddressfoo@example.comwhileitisalreadyassociatedwithanexistingaccount.Perhapsyouactuallywantedtologinortoresetyourpassword?Ifitactuallywasn’tyou,pleaseletusknowbyreplyingtothisemailandwe’llinvestigatethis.

Sincerely,ExampleCompany

Page 264: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Finally,youmightalsowanttoconsiderinvalidatingordeduplicatinge-mailsthatcontainthe“+”characterintheusernamepart,followedbyasequenceofcharacters,representingane-mailalias.Foralotofe-mailproviders,notablyGmail,[email protected][email protected],therebybasicallyallowingtheendusertocreateanearlyunlimitedamountofaccounts.

CustomConstraintsWhilenotpartoftheJSF,forthesakeofcompletenesswe’dliketoshowanotherexampleofacustomBeanValidationconstraint.Anearlierexamplewasalreadygiveninthesectionabout<f:validateWholeBean>.TheBeanValidationAPIalreadyoffersalotofexistingconstraintsoutoftheboxwhichyoucanfindinthejavax.validation.constraintspackage. AlotofnewconstraintshavebeenaddedinBeanValidation2.0,alsopartofJavaEE8likeJSF2.3,suchas@Email.

MostcommonusecasesforacustomBeanValidationconstraintarerelatedtolocalizedpatterns.Thinkofphonenumbers,zipcodes,bankaccountnumbers,andpasswords.Ofcourse,mostofthosecouldbedonewithjusta@Pattern,butthismayendupinlessself-documentingcode,particularlyifthedesiredpatternisrelativelycomplex.

Followingisanexampleofacustom@Phoneconstraintwhichshouldmatchasmanyaspossibleinternationallyknownphonenumbers:

@Constraint(validatedBy=PhoneValidator.class)

25

Page 265: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Target(FIELD)

@Retention(RUNTIME)

public@interfacePhone{

Stringmessage()default"Invalidphonenumber";

Class<?>[]groups()default{};

Class<?>[]payload()default{};

}

Andhere’stheassociatedPhoneValidator:publicclassPhoneValidator

implementsConstraintValidator<Phone,String>

{

privatestaticfinalPatternSPECIAL_CHARS=

Pattern.compile("[\\s().+-]|ext",

Pattern.CASE_INSENSITIVE);

privatestaticfinalPatternDIGITS=

Pattern.compile("[0-9]{7,15}");

@Override

publicbooleanisValid

(Stringphone,ConstraintValidatorContext

context)

{

if(phone==null||phone.isEmpty()){

returntrue;//Let@NotNull/@NotEmpty

handlethis.

}

returnisValid(phone);

}

publicstaticbooleanisValid(Stringphone){

Page 266: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Stringdigits=

SPECIAL_CHARS.matcher(phone).replaceAll("");

returnDIGITS.matcher(digits).matches();

}

}

Inordertoactivateit,simplyannotatetheassociatedentityproperty.

@Phone

privateStringphone;

ThiswillbetriggeredonboththeJSFandJPAsides:inJSF,duringtheprocessvalidationsphase(thirdphase);inJPAduringthepersistandmerge.Asnotedinthe<f:validateBean>/<f:validateWholeBean>section,itcanbedisabledonbothsides.

CustomMessagesConversionandvalidationerrormessagescomingfromJSFaswellasBeanValidationarefullycustomizable.Application-wide,theycanbecustomizedbysupplyingapropertiesfilewhichspecifiesthedesiredmessageasthevalueofapredefinedkey.YoucanfindpredefinedkeysforJSFconversionandvalidationmessagesinChapter2.5.2.4,“LocalizedApplicationMessages,”oftheJSF2.3specification. YoucanfindpredefinedkeysforBeanValidationmessagesinAppendixB,“StandardResourceBundleMessages,”oftheBeanValidation2.0specification. ForJSF,thefullyqualifiednameofthepropertiesfilemustberegisteredas<message-bundle>in

26

27

Page 267: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

faces-config.xml.ForBeanValidation,theexactfullyqualifiednameofthepropertiesfileisValidationMessages.

Asanexample,we’regoingtomodifythedefaultmessageoftheJSFrequired="true"validationandtheBeanValidation@NotNullconstraint.

main/java/resources/com/example/project/i18n/messages.propert

ies

javax.faces.component.UIInput.REQUIRED={0}isrequired.

javax.faces.validator.BeanValidator.MESSAGE={1}{0}

main/java/resources/ValidationMessages.properties

javax.validation.constraints.NotNull.message=isrequired.

NotetheabsenceofthelabelplaceholderintheBeanValidationmessage.Instead,the{1}ofthejavax.faces.validator.BeanValidator.MESSAG

ErepresentsthelabelassociatedwiththeJSFcomponentand{0}representstheBeanValidationmessage.ThecustomBeanValidationmessagebundlefileisalreadyautomaticallypickedup.ThecustomJSFmessagebundlefileneedstobeexplicitlyregisteredinthefaces-config.xmlfirst.

<application>

<message-

bundle>com.example.project.i18n.messages</message-bundle>

</application>

Withthosepropertiesfilesinplace,thefollowinginputcomponentswillthusshowexactlythesamevalidationerrormessage:<h:inputTextid="field"label="Firstinput"value="#{bean.field}"required="true">

Page 268: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</h:inputText>

<h:messagefor="field"/>

<h:inputTextid="notNullField"label="Secondinput"

value="#{bean.notNullField}">

</h:inputText>

<h:messagefor="notNullField"/>

Incaseyouwanttofine-grainthemessageonaper-componentbasis,youcanusetheconverterMessage,validatorMessage,and/orrequiredMessageattributeoftheUIInputcomponent.TheconverterMessagewillbedisplayedonanyconversionerror.

<h:inputTextvalue="#{bean.localDate}"

converterMessage="PleaseenterdateinpatternYYYY-MM-

DD.">

<f:convertLocalDatetype="localDate"pattern="yyyy-MM-dd"

/>

</h:inputText>

ThevalidatorMessagewillbedisplayedonanyvalidationerror,aswellasthosetriggeredbyBeanValidation.

<h:inputTextvalue="#{bean.dutchZipCode}"required="true"

validatorMessage="Pleaseenterzipcodeinpattern

1234AB.">

<f:validateRegexpattern="[0-9]{4}[A-Z]{2}"/>

</h:inputText>

Notethatthiswon’tbeshownwhenrequired="true"isn’tsatisfied.Forthat,youneedtouserequiredMessage

Page 269: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

instead.

<h:inputTextvalue="#{bean.dutchZipCode}"required="true"

requiredMessage="Pleaseenterzipcode."

validatorMessage="Pleaseenterzipcodeinpattern

1234AB.">

<f:validateRegexpattern="[0-9]{4}[A-Z]{2}"/>

</h:inputText>

Notethatthiswon’tbeshownforanyBeanValidation@NotNull.YoushouldthenusevalidatorMessageinstead.

Footnotes1https://javaee.github.io/javaee-

spec/javadocs/javax/faces/convert/package-summary.html.

2

https://javaserverfaces.github.io/docs/2.3/vdldoc/f/conv

ertNumber.html.

3

https://docs.oracle.com/javase/8/docs/api/java/text/Numb

erFormat.html.

4http://floating-point-gui.de.

5

https://docs.oracle.com/javase/8/docs/api/java/text/Deci

malFormat.html.

6

https://javaserverfaces.github.io/docs/2.3/vdldoc/f/conv

ertDateTime.html.

7

Page 270: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

https://docs.oracle.com/javase/8/docs/api/java/text/Date

Format.html.

8

https://docs.oracle.com/javase/8/docs/api/java/time/form

at/DateTimeFormatter.html.

9https://en.wikipedia.org/wiki/ISO_8601.

10

https://docs.oracle.com/javase/8/docs/api/java/text/Simp

leDateFormat.html.

11

https://docs.oracle.com/javase/8/docs/api/java/time/form

at/DateTimeFormatter.html.

12https://developer.mozilla.org/en-

US/docs/Web/HTML/Element/input/date.

13https://developer.mozilla.org/en-

US/docs/Web/HTML/Element/input/date.

14https://developer.mozilla.org/en-

US/docs/Web/HTML/Element/input/time.

15https://developer.mozilla.org/en-

US/docs/Web/HTML/Element/input/datetime-local.

16https://javaee.github.io/javaee-

spec/javadocs/javax/faces/validator/package-summary.html

.

17

https://docs.oracle.com/javase/8/docs/api/java/util/rege

x/Pattern.html.

18http://hibernate.org/validator/.

19https://javaee.github.io/javaee-

Page 271: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

spec/javadocs/javax/persistence/ValidationMode.html.

20https://javaee.github.io/javaee-

spec/javadocs/javax/faces/convert/Converter.html..

21https://stackoverflow.com/q/7665673/157882..

22https://javaee.github.io/javaee-

spec/javadocs/javax/faces/convert/Converter.html..

23https://html.spec.whatwg.org/multipage/form-

elements.html#attr-option-value..

24https://javaee.github.io/javaee-

spec/javadocs/javax/faces/validator/Validator.html..

25https://javaee.github.io/javaee-

spec/javadocs/javax/validation/constraints/package-

summary.html.

26http://download.oracle.com/otn-pub/jcp/jsf-2_3-final-

eval-spec/JSF_2.3.pdf.

27http://beanvalidation.org/2.0/spec/#standard-resolver-

messages.

Page 272: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1) (2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_6

6.OutputComponents

BaukeScholtz andArjanTijms

Willemstad,Curaçao Amsterdam,Noord-Holland,TheNetherlands

Technically,theinputcomponentsasdescribedinChapter4arealsooutputcomponents.Theyarenotonlycapableofprocessinganysubmittedinputvaluesbutalsocapableofoutputtingthemodelvalueduringtherenderresponsephase(sixthphase).ThisisalsovisibleintheJSF(JavaServerFaces)API(applicationprogramminginterface):theUIInputsuperclassextendsfromtheUIOutputsuperclass.

TherearealsoabunchofcomponentsthatmerelyoutputtheirmodelvalueorevenjustanHTMLelement.Thosearethepureoutputcomponents.Theydon’tparticipateinallphasesoftheJSFlifecycle.Sometimestheyparticipateduringtherestoreviewphase(firstphase),incasetheyaredynamicallycreatedormanipulated,butthemajorityoftheirjobisexecutedduringtherenderresponsephase(sixthphase),whilegeneratingtheHTMLoutput.Duringtheotherphases,theydon’tdomanyadditionaltasks.

Document-BasedOutputComponents

1 2

Page 273: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Thesecomponentsare<h:doctype>,<h:head>,and<h:body>.Notethatthere’snosuchcomponentas<h:html>.<h:doctype>isarguablytheleastusedHTMLcomponentoftheentirestandardJSFHTMLcomponentset.Youcouldgetawaywithjustaplain<!DOCTYPEhtml>element.<h:doctype>isonlyusefulwhenyouwanttohaveapureXMLrepresentationofthe<!DOCTYPE>element,whichisgenerallyonlythecasewhenyouneedtostoreentireJSFviewsaspartofanotherXMLstructureofsomehigher-levelabstractlayeraroundJSF.

<h:head>and<h:body>are,sinceJSF2.0,themostimportanttagsafter<f:view>becameoptionalinFacelets.Historically,<f:view>wasmandatoryinJSPinordertodeclaretheJSPpagebeingaJSFview.Whilegeneratingthe<head>and<body>elementsoftheHTMLdocumentdoesn’trequireanyspeciallogic,and<h:head>and<h:body>aren’tmandatoryforaFaceletspageinordertoberecognizedasaJSFview,thosetagsaremandatoryfortheproperautomatichandlingofJavaScriptandCSS(CascadingStyleSheets)resourcedependencies,alsointroducedinJSF2.0.

<h:head>and<h:body>allowJSFtoautomaticallyrelocateJavaScriptandCSSresourcedependenciestotherightplacesinthecomponenttreesothattheyultimatelyendupintherightplaceinthegeneratedHTMLoutput.FromthestandardJSFcomponentsetonly<h:commandLink>,<h:commandScript>,<f:ajax>,and<f:websocket>utilizethisfacility.Theyallrequirethejsf.jsJavaScriptfilebeingincludedinthefinalHTMLdocument.Duringtheviewbuildtime,theywillbasicallyuse

1

Page 274: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

UIViewRoot#addComponentResource() toregisterthecomponentresourcedependencyatthespecifiedtargetcomponent,whichcanbeeither<h:head>or<h:body>.Duringtheviewrendertime,therendererassociatedwiththe<h:head>and<h:body>componentwillobtainallsofarregisteredcomponentresourcedependenciesbyUIViewRoot#getComponentResources() andgeneratetheappropriate<linkrel="stylesheet">and<script>elementswithaURL(uniformresourcelocator)referringtheassociatedresourcedependency.

Asshowninthesection“StandardHTMLComponents”inChapter3,thefollowingcodeiswhatthemostminimalandHTML5-validJSFpagelookslike:

<!DOCTYPEhtml>

<htmllang="en"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

>

<h:head>

<title>Title</title>

</h:head>

<h:body>

...

</h:body>

</html>

Text-BasedOutputComponentsThesecomponentsare<h:outputText>,<h:outputFormat>,<h:outputLabel>,and<h:outputLink>.TheyallextendfromtheUIOutputsuperclassandhaveavalueattributewhichcanbeboundto

1

2

Page 275: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

amanagedbeanproperty.Duringtheviewrendertime,thegetterwillbeconsultedtoretrieveanddisplayanypresetvalue.Thesecomponentswillneverinvokethesettermethodandthereforeitcouldbesafelyleftoutofthemanagedbeanclassinordertoreduceunusedcode.

Historically,inJSF1.xonJSP(JavaServerPages),<h:outputText>wasmandatoryinordertooutputabeanpropertyastext.JSPdidn’tsupportJSF-styleEL(ExpressionLanguage)#{...}intemplatetext.FaceletssupportedJSF-styleEL#{...}intemplatetextandhencebeanpropertiescouldbeoutputteddirectlyinFaceletswithouttheneedforawholeJSFcomponent.Inotherwords,thefollowingcodesareequivalentinJSFonFacelets,<h:outputText>:<p>Welcome,<h:outputTextvalue="#{user.name}">!<p>

AndELintemplatetext:

<p>Welcome,#{user.name}!</p>

Itdoesn’tneedexplanationthatthelattercodesnippetismoreterseandreadable.<h:outputText>has,however,notbecomeuselessinFacelets.It’sstillusefulforthefollowingpurposes:

DisablingimplicitHTMLescaping.

Attachinganexplicitconverter.

Referencingin<f:ajaxrender>.

JSFhasimplicitHTMLescapingeverywhere.AnythingoutputtedtotheHTMLresponseischeckedontheHTMLspecialcharacters“<”,“>”,“&”,andoptionallyalso“"”whenoutputtedwithinanattributeofanHTMLelement.Those

Page 276: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

HTMLspecialcharacterswillbereplacedby“<”,“>”,“&”,and“&quot;”,respectively.ThewebbrowserwillthennotinterpretthosecharactersaspartofthegeneratedHTMLoutputbutasplaintextand,ultimately,willpresentthemasliteralcharacterstotheenduser.

Imaginethatauserchooses<script>alert('xss')</script>asausername,andit’semittedvia#{user.name}througheitheroneoftheaboveshowncodesnippets;thenJSFwillrenderitasfollowsinthegeneratedHTMLoutput:<p>Welcome,<script>alert('xss')</script>!</p>

Andthewebbrowserwilldisplayitliterallyas“Welcome,<script>alert('xss')</script>!”insteadofonly“Welcome,!”alongwithaJavaScriptalertwiththetext“xss”wherebytheuser-controlledJavaScriptisunintentionallyactuallyexecuted.TheenduserbeingabletoexecutearbitraryJavaScriptcodeisdangerous.Itwouldallowthemalicioususertoexecutespecificcodewhichtransfersinformationaboutsessioncookiestoanexternalhostwhensomeoneelselogsinandviewsapagewhereintheusernameofthemalicioususerisbeingrendered.(Seealsothesection“Cross-SiteScriptingProtection”inChapter13.)Ontheotherhand,theremayalsobecaseswherebyyou’dliketoembedsafeHTMLcodeinthegeneratedHTMLoutput.Mostcommonusecasesarerelatedtopostingmessagesforotherusersonawebsite,wherebyalimitedsubsetofformattingisallowed,suchasbold,italics,links,lists,headings,etc.Generally,thosearetobeenteredinatextareaelementusingapredefinedhuman-friendlymarkupformat,suchasMarkdown,orthelesserknownWikicode,ortheancientBBCode.Theyareallcapableofparsingtheraw

Page 277: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

textwiththemarkupandconvertingittosafeHTMLcodewherebyanymaliciousHTMLcodeisalreadyescapedorstrippedout.

<h:inputTextareavalue="#{message.text}"/>

Therawtextisatleastalwayssavedinthedatabasefortherecord,andtheresultingsafeHTMLcode,alongwiththeversionoftheparserused,canalsobesavedinthedatabaseforperformance,sothattheparserdoesn’tneedtobeunnecessarilyre-executedforthesamepieceofrawtext.Giventhatwe’regoingtouseMarkdownwithCommonMarkandhavingthefollowingMarkdowninterface,

privateinterfaceMarkdown{

publicStringgetText();

publicvoidsetHtml(Stringhtml);

publicStringgetVersion();

publicvoidsetVersion(Stringversion);

}

AndthefollowingMarkdownListenerentitylistener,publicclassMarkdownListener{

privatestaticfinalParserPARSER=

Parser.builder().build();

privatestaticfinalHtmlRendererRENDERER=

HtmlRenderer.builder().escapeHtml(true).build();

privatestaticfinalStringVERSION=

getCommonMarkVersion();

@PrePersist

3

Page 278: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicvoidparseMarkdown(Markdownmarkdown){

Stringhtml=

RENDERER.render(PARSER.parse(markdown.getText()));

markdown.setHtml(html);

markdown.setVersion(VERSION);

}

@PreUpdate

publicvoidparseMarkdownIfNecessary(Markdown

markdown){

if(markdown.getVersion()==null){

parseMarkdown(markdown);

}

}

@PostLoad

publicvoidupdateMarkdownIfNecessary(Markdown

markdown){

if(!VERSION.equals(markdown.getVersion())){

parseMarkdown(markdown);

}

}

privatestaticStringgetCommonMarkVersion(){

try{

Propertiesproperties=newProperties();

properties.load(Parser.class.getResourceAsSt

ream(

"/META-

INF/maven/com.atlassian.commonmark"

+"commonmarkpom.properties"));

Page 279: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

returnproperties.getProperty("version");

}

catch(IOExceptione){

thrownewUncheckedIOException(e);

}

}

}

thentheMessageentityimplementingtheMarkdowninterfaceandregisteredwiththeMarkdownListenerentitylistenercanlookasfollows:

@Entity@EntityListeners(MarkdownListener.class)

publicclassMessageimplementsMarkdown,Serializable{

@Id@GeneratedValue(strategy=IDENTITY)

privateLongid;

@Column(nullable=false)@Lob

private@NotNullStringtext;

@Column(nullable=false)@Lob

privateStringhtml;

@Column(nullable=false,length=8)

privateStringversion;

@Override

publicvoidsetText(Stringtext){

if(!text.equals(this.text)){

this.text=text;

setVersion(null);//TriggerforMarkdownListener

@PreUpdate.

}

}

//Add/generateremaininggettersandsetters.

Page 280: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

Finally,inordertopresentthesafeHTMLcodetotheenduser,youcanuse<h:outputText>withtheescapeattributesettofalse,wherebyyouthusinstructJSFthatitdoesn’tneedtoimplicitlyHTML-escapethevalue.

<h:outputTextvalue="#{message.html}"escape="false"/>

NexttoimplicitHTMLescaping,JSFalsosupportsimplicitconversion.Foranypropertytypewhichisemittedvia<h:outputText>orevenELintemplatetext,JSFwilllookuptheconverterbyclass,invokeitsConverter#getAsString()method,andrendertheresult.Incaseyouwanttoexplicitlyuseaspecificoradifferentconverter,youhavetoreplaceanyELintemplatetextby<h:outputText>andexplicitlyregistertheconverteronit.Generally,itisthosekindsofnumber-ordatetime-relatedpropertiesthatneedtobeformattedinalocale-specificpattern.

<h:outputTextvalue="#{product.price}">

<f:convertNumbertype="currency"locale="en_US"/>

</h:outputText>

Thelastpurposeof<h:outputText>isbeingabletoreferenceapieceofinlinetextin<f:ajaxrender>.Bydefault,<h:outputText>doesn’tgenerateanyHTMLcode.ButifithasatleastanattributespecifiedwhichmustendupinthegeneratedHTMLoutput,suchasidorstyleClass,thenitwillgenerateanHTML<span>

Page 281: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

element.ThisisreferenceableviaJavaScriptandthususefulforAjax-updatingspecificpartsoftext.Ofcourse,youcouldalsooptforAjax-updatingsomecommoncontainercomponent,butthisisfarlessefficientthanAjax-updatingonlyspecificpartswhichreallyneedtobeupdated.

<h:outputFormat>isanextensionof<h:outputText>whichparsesthevalueusingjava.text.MessageFormatAPI beforehand.Thisisparticularlyusefulincombinationwithlocalizedresourcebundles.Anexamplecanbefoundinthesection“ParameterizedResourceBundleValues”inChapter14.

<h:outputLabel>basicallygeneratestheHTML<label>element,whichisanessentialpartofHTMLforms.Thiswasalreadydescribedinthesection“LabelandMessageComponents”inChapter4.Itisimportanttonotethat<h:outputLabel>and<h:outputText>are,inHTMLperspective,absolutelynotinterchangeable.Inarelativelyrecentburstoflow-qualityprogrammingtutorialsitesontheInternetwhichbasicallyshowcodesnippetswithoutanytechnicalexplanationforthesakeofadvertisementincomes,<h:outputLabel>isoftenincorrectlybeingusedtooutputapieceoftextinaHelloWorldJSFpage.Suchtutorialsitescanbetterbeignoredentirely.

<h:outputLink>generatesanHTML<a>element.It’ssomewhataleftoverofJSF1.xandisn’tterriblyusefulsincetheintroductionofthemuchmoreuseful<h:link>inJSF2.0.Whenyoudon’tneedtoreferenceaJSFviewwithalink,forwhichyou’duse<h:link>instead,youcouldaswelljustuseaplainHTML<a>elementinsteadof<h:outputLink>.Thefollowingtagsgenerateexactlythe

4

Page 282: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

sameHTML.

<h:outputLinkvalue="http://google.com">Google</h:outputLink>

<ahref="http://google.com">Google</a>

TheplainHTMLequivalentisterser.

Navigation-BasedOutputComponentsThesecomponentsare<h:link>and<h:button>,bothextendingfromtheUIOutcomeTargetsuperclass.TheyhaveanoutcomeattributewhichacceptsalogicalpathtoaJSFview.Thepathwillactuallybevalidatedifit’savalidJSFview;otherwise,thelinkorbuttonwillberenderedasdisabled.Inotherwords,theydon’tacceptapathtoanon-JSFresource,letaloneanexternalURL.Forthis,you’dneed<h:outputLink>orplainHTMLinstead.

<h:link>willgenerateanHTML<a>elementwiththeURLofthetargetJSFviewspecifiedasanhrefattribute.<h:button>willgenerateanHTML<inputtype="button">elementwithanonclickattributewhichassigns,withhelpofJavaScript,theURLofthetargetJSFviewtowindow.location.hrefproperty.Thisisindeedsomewhatawkward,butthat’sjustalimitationofHTML.Neither<inputtype="button">nor<button>supportsanhref-likeattribute.

GiventhefollowingfolderstructureinaMavenWAR

Page 283: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

projectinEclipse,

Thefollowing<h:link>and<a>pairsenclosedinfolder1page1.xhtmlwillallgenerateexactlythesamelinks.

<h:linkoutcome="page2"value="link1"/>

<ahref="#{request.contextPath}folder1page2.xhtml">link1</a>

<h:linkoutcome="folder2page1"value="link2"/>

<ahref="#{request.contextPath}folder2page1.xhtml">link2</a>

<h:linkoutcome="folder2page2"value="link3"/>

<ahref="#{request.contextPath}folder2page2.xhtml">link3</a>

<h:linkoutcome="page1"value="link4">

<ahref="#{request.contextPath}/page1.xhtml">link4</a>

<h:linkoutcome="page2"value="link5">

<ahref="#{request.contextPath}/page2.xhtml">link5</a>

Notethusthat<h:link>alreadyautomaticallyprependsanycontextpathofthewebapplicationprojectandappendsthe

Page 284: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

currentlyactiveURLpatternoftheFacesServletmapping.Alsonotethatwithouttheleadingslash,theoutcomeisinterpretedrelativetothecurrentfolder,andwithaleadingslash,theoutcomeisinterpretedrelativetothecontextpath.

Panel-BasedOutputComponentsThesecomponentsare<h:panelGroup>and<h:panelGrid>,bothextendingfromtheUIPanelsuperclass.<h:panelGroup>hasmultipleresponsibilities.ItcangenerateanHTML<span>,or<div>,oreven<td>,dependingonthelayoutattributeandwhetherit’senclosedina<h:panelGrid>.

Bydefault,<h:panelGroup>generatesjustanHTML<span>element,like<h:outputText>.Themaindifferenceisthat<h:panelGroup>doesn’thaveavalueattribute.Instead,thecontentisrepresentedbyitschildren.Italsodoesn’tsupportdisablingHTMLescapingorattachingaconverter.That’suptoany<h:outputText>child.Inthiscontext,it’snotterriblyuseful.<h:panelGroup>isonlymoreusefulthan<h:outputText>whenyouneedtobeabletoreferenceusing<f:ajaxrender>aninlineelementwhichinturngroupsabunchofcloselyrelatedinlineelements.Somethinglikethefollowingrepresentsthe“userprofile,”whichshouldbeAjax-updatablefromwithinsomesortofuserprofileeditpage.

<p>

Welcome,

<h:panelGroupid="userProfile">

<imgsrc="#{user.imageUrl}"/>

Page 285: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

#{user.name}

</h:panelGroup>

</p>

...

<h:form>

...

<f:ajax...render=":userProfile"/>

...

</h:form>

Whensettingthelayoutattributeof<h:panelGroup>toblock,thenitwillgenerateanHTML<div>element.InstandardHTML,“inlineelements” don’tstartatanewlinebydefaultanddon’tallowblockelementchildren.And,“block-levelelements” alwaysstartatanewlinebydefaultandallowinlineaswellasblockelementsaschildren.Hencethesupportedvaluesofthelayoutattributeof<h:panelGroup>are“inline”and“block”.Historically,thelayoutattributewasonlyaddedinJSF1.2aftercomplaintsfromJSFdevelopersaboutamissingJSFcomponenttorepresentanHTML<div>element.(SeealsoChapter7.)ThiscouldbeusedtowraplargersectionswhichneedtobeAjax-updatable;otherwiseaplainHTML<div>isalsosufficient.

<h:panelGrouplayout="block"id="userProfile">

<p>

Welcome,

<imgsrc="#{user.imageUrl}"/>

#{user.name}

</p>

</h:panelGroup>

Notethatit’sillegalinHTMLtohaveablockelementnested

5

6

Page 286: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

inaninlineelement.The<p>isablockelementandhencethelayout="block"isabsolutelymandatoryintheaboveconstruct.Ifyoudon’tspecifythisattributeandthuseffectivelyletJSFrenderanHTML<span>element,thenthewebbrowserbehaviorisunspecified.TheaveragewebbrowserwillrendertheblockelementchildrenoutsidetheinlineelementandevenpossiblyerroroutwhenthisconstructismanipulatedbyJavaScript,suchasduringaJSFAjaxupdateaction.

Alsokeepinmindthatintheaboveconstruct,the<p>tagsandthe“Welcome”textarealsoupdatedduringanyJSFAjaxupdateactiononthe<h:panelGroup>.Thisisessentiallyawasteofhardwareresources,onboththeserversideandtheclientside,asthosearestaticandneversubjecttochanges.WhenAjax-updatingthings,youshouldpreferablyensurethat<f:ajaxrender>onlyreferencescomponentsthatabsolutelyneedtobeAjax-updatedandthusnotanunnecessarilylargesection.

When<h:panelGroup>isbeingnestedina<h:panelGrid>component,whichgeneratesanHTML<table>element,thenthelayoutattributeof<h:panelGroup>isignoredandthecomponentwillbasicallyactasacontainerofcomponentswhichshouldultimatelyendupintheverysamecellofthetable.Thatis,therendererof<h:panelGrid>considerseverydirectchildcomponentasanindividualtablecell.

Giventhefollowingtwo-column<h:panelGrid>,whichshouldgenerateatwo-columnHTMLtable,whatwouldyouguesstheactualgeneratedHTMLoutputshouldlooklike?

Page 287: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:panelGridcolumns="2">

one

<h:outputTextvalue="two"/>

three

four

<h:panelGroup>five</h:panelGroup>

six

seven

<h:panelGroup>

eight

nine

</h:panelGroup>

</h:panelGrid>

Hint

EachsectionoftemplatetextbetweentwoJSFcomponentsisinternallyconsideredasingleJSFcomponent.InMojarra,it’srepresentedbytheinternalUIInstructionscomponent.Theactualcomponenttreehierarchyisthusroughlyrepresentedasbelow.

<h:panelGridcolumns="2">

<ui:instructions>one</ui:instructions>

<h:outputTextvalue="two"/>

<ui:instructions>

three

four

</ui:instructions>

<h:panelGroup>five</h:panelGroup>

<ui:instructions>

six

seven

</ui:instructions>

<h:panelGroup>

eight

nine

Page 288: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</h:panelGroup>

</h:panelGrid>

Noteagainthatthere’snosuchcomponentas<ui:instructions>inFacelets.Theabovemarkupispurelyforvisualizationsothatyourbraincanbetterprocessit.This<h:panelGrid>hasthuseffectivelysixdirectchildrenwhichwilleachendupintheirowntablecell.Withtwocolumns,thiswillthuseffectivelygeneratethreerows.Here’stheactualgeneratedHTMLoutput(reformattedforreadability).

<table>

<tbody>

<tr>

<td>one</td>

<td>two</td>

</tr>

<tr>

<td>threefour</td>

<td>five</td>

</tr>

<tr>

<td>sixseven</td>

<td>eightnine</td>

</tr>

</tbody>

</table>

RenderinginChromebrowser:

Yousee,<h:panelGroup>makessurethat“five”and

Page 289: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

“eightnine”don’tendupintheverysametablecellas“sixseven.”Alsonotethatit’sunnecessarytowrapanyJSFcomponentin<h:panelGroup>ifitshouldrepresentasinglecellalready.Therefore,<h:outputText>behind“two”doesn’tneedtobewrappedin<h:panelGroup>.Youcan,ofcourse,dosoforbettersourcecodereadability,butthisistechnicallyunnecessary.

Ifyouhappentohaveadynamicamountofcellsbasedonaview-scopedmodel,thenyoucannestaJSTL(JSPStandardTagLibrary)<c:forEach>in<h:panelGrid>tohaveitgeneratethemasadatagridwithafixedamountofcolumns.

<h:panelGridcolumns="3">

<c:forEachitems="#{viewProducts.products}"

var="product">

<h:panelGroup>

<h3>#{product.name}</h3>

<p>#{product.description}</p>

</h:panelGroup>

</c:forEach>

</h:panelGrid>

Notethusthat<ui:repeat>isunsuitablehereascomparedto<c:forEach>,asexplainedinthesection“JSTLCoreTags”inChapter3.Itwilltechnicallyworkjustfine,buttherendererof<h:panelGrid>willinterpretitasasingletablecell.

Alsonotethatit’sveryimportantforthemodeltobeviewscoped,particularlyifyouhaveJSFformcomponentsinside<h:panelGrid>.Thetechnicalreasonisthatduringprocessingthepostbackrequest,JSFexpectsthemodelitembehindtheiterationindextobeexactlythesameasitwas

Page 290: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

whenthepagewaspresentedtotheenduser.Inotherwords,whenJSFisabouttoprocessaformsubmit,andanitemhasbeenaddedorremovedorevenreorderedinthemeanwhile,causingtheiterationindextobechanged,thesubmittedvaluesand/ortheinvokedactionwouldpossiblybeperformedagainstthewrongitemcurrentlyattheinitiallyknownindex.Thisisdangerousfortheintegrityofthemodel.Ifyoudon’thaveanyJSFformcomponentsinside<h:panelGrid>,orifthemodelisn’tsubjecttochangesduringtheviewscope,e.g.,becauseit’sonlycreatedorupdatedduringapplicationstartup,thenthebackingbeanbehind#{viewProducts}cansafelyberequestscoped.

DataIterationComponentYes,there’sonlyone,<h:dataTable>,whichextendsfromtheUIDatasuperclassandgeneratesanHTML<table>basedonaniterabledatamodelwherebyeachitemisrepresentedasasinglerow.TheotherdataiterationcomponentavailableinJSF,theFacelets<ui:repeat>,doesn’textendfromtheUIDatasuperclassanddoesn’temitanyHTMLoutputandthereforedoesn’ttechnicallycountasan“outputcomponent.”Also,inthestandardJSFcomponentsetnosuchcomponentgeneratesanHTML<ul>,<ol>,or<dl>,butthiscanrelativelyeasilybecreatedasacustomcomponentextendingfromUIData.(Seealsothesection“CreatingNewComponentandRenderer”inChapter11.)ThevalueattributeofUIDatasupportsjava.lang.Iterable.Inotherwords,youcansupplyanyJavacollectionasamodelvalue.Asindex-basedaccessis

Page 291: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

mostusedinUIData,mostefficientisthejava.util.ArrayListasitoffersO(1)accessbyindex.Therendererofthe<h:dataTable>componentsupportsonly<h:column>asadirectchildcomponent.Anythingelseisignored.Asitsnamehints,<h:column>representsasinglecolumn.Eachiterationroundoverthevalueof<h:dataTable>willbasicallyre-renderallcolumnsagainstthecurrentlyiterateditem.Aswith<c:forEach>and<ui:repeat>,thecurrentlyiterateditemisexposedinELscopebythevarattribute.FollowingisabasicexamplewhichiteratesoveraList<String>.

<h:dataTableid="strings"value="#{bean.strings}"

var="string">

<h:column>#{string}</h:column>

</h:dataTable>

Backingbeanclasscom.example.project.view.Bean:@Named@RequestScoped

publicclassBean{

privateList<String>strings;

@PostConstruct

publicvoidinit(){

strings=Arrays.asList("one","two","three");

}

publicList<String>getStrings(){

returnstrings;

Page 292: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

}

GeneratedHTMLoutput:

<table>

<tbody>

<tr><td>one</td></tr>

<tr><td>two</td></tr>

<tr><td>three</td></tr>

</tbody>

</table>

RenderinginChromebrowser:

Itisimportanttonotethatthevariablenameasspecifiedbythevarattributeshouldn’tclashwithexistingmanagedbeannamesorevenwithimplicitELobjects.ImplicitELobjectshavehigherprecedenceinELresolving.OneexampleofanimplicitELobjectis#{header}whichreferstoExternalContext#getRequestHeaderMap(). Sowhenyouhappentohave#{bean.headers}andyou’dliketopresentitinaniteratingcomponent,thenyoucan’tusevar="header"andyou’dbetterthinkofadifferentname,suchasvar="head".

Followingisamoreelaborateexamplewhichshowsalistofproducts.AsimilartablewasshownearlierinChapter5.

<h:dataTableid="products"value="#{products.list}"

var="product">

<h:column>

7

Page 293: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:facetname="header">ID</f:facet>

#{product.id}

</h:column>

<h:column>

<f:facetname="header">Name</f:facet>

#{product.name}

</h:column>

<h:column>

<f:facetname="header">Description</f:facet>

#{product.description}

</h:column>

</h:dataTable>

Backingbeanclasscom.example.project.view.Products:@Named@RequestScoped

publicclassProducts{

privateList<Product>list;

@Inject

privateProductServiceproductService;

@PostConstruct

publicvoidinit(){

list=productService.list();

}

publicList<Product>getList(){

returnlist;

}

}

Productentity:com.example.project.model.

Page 294: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Product:

@Entity

publicclassProduct{

@Id@GeneratedValue(strategy=IDENTITY)

privateLongid;

@Column(nullable=false)

private@NotNullStringname;

@Column(nullable=false)

private@NotNullStringdescription;

//Add/generategetters+setters.

}

Productservice:com.example.project.service.ProductService

:@StatelesspublicclassProductService{

@PersistenceContext

privateEntityManagerentityManager;

@TransactionAttribute(SUPPORTS)

publicList<Product>list(){

returnentityManager

.createQuery("FROMProductORDERBYid

DESC",Product.class)

.getResultList();

}

}

Page 295: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

GeneratedHTMLoutput:

<table>

<thead>

<tr>

<thscope="col">ID</th>

<thscope="col">Name</th>

<thscope="col">Description</th>

</tr>

</thead>

<tbody>

<tr>

<td>3</td>

<td>Three</td>

<td>Thethirdproduct</td>

</tr>

<tr>

<td>2</td>

<td>Two</td>

<td>Thesecondproduct</td>

</tr>

<tr>

<td>1</td>

<td>One</td>

<td>Thefirstproduct</td>

</tr>

</tbody>

</table>

RenderinginChromebrowser:

Itisimportanttonotethatthemodelbehindthevalueattributeof<h:dataTable>mustreferabeanpropertywhichisalreadypreparedbeforehandinaone-timelifecycle

Page 296: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

event,suchas@PostConstructor<f:viewAction>.Thisdoesn’tapplyspecificallytoUIDatacomponentsbuttobasicallyeveryJSFcomponent.Thatis,thegettermethodmaybeinvokedmultipletimesduringtheJSFlifecycle,especiallywhenreferencedinthevalueattributeofaniterationcomponentorintherenderedattributeofanyJSFcomponent.

ThetechnicalreasonisthatanyELvalueexpressionis,behindthescenes,createdasajavax.el.ValueExpressioninstancewhichinternallybasicallyjustholdstheliteralELstringsuchas#{products.list}andanyValueExpression#getValue()callonitwouldsimplyre-evaluatetheexpressionagainsttheprovidedELcontext.Thisisnormallyaverycheapoperation,doneinnanoseconds,butitmayslowdowndrasticallywhenthegettermethodinturnperformsarelativelyexpensivedatabasequerywhichmaytaketensorevenhundredsofmilliseconds.

IterationcomponentsmayinvokethegettermethodduringeveryphaseoftheJSFlifecyclewhentheiterationcomponenthappenstohaveformcomponentsnested.Whenyoupreparethemodelbyobtainingalistfromthedatabaseinthegettermethod,thiswouldcausethedatabasetobequeriedoneverysinglegettermethodcall,whichisplainlyinefficient.Moreover,thesameproblemswithregardtoresolvingtheiterateditemofinterestbasedontheiterationindexmayoccurasdescribedinthelastparagraphoftheprevioussectionabout<h:panelGrid>with<c:forEach>.

Anotherthingtonoteis<f:facetname="header">.Thisgeneratesbasically<thead>with

Page 297: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

thecontentin<th>.<h:dataTable>alsosupports<f:facetname="footer">whichwillthengeneratethe<tfoot>withthecontentin<td>.Youcanusuallyfindallsupported<f:facet>namesinthetagdocumentation,aswellasinthe<h:dataTable>tagdocumentation.

Basically,youcanputanythinginside<h:column>torepresentthecellcontent.Evenformcomponentsoranested<h:dataTable>or<ui:repeat>.FollowingisasmallexamplewhichshowsafictiveSet<Tag>tagspropertyofProductentityinanested<ui:repeat>.

<h:dataTableid="products"value="#{products.list}"

var="product">

...

<h:column>

<ui:repeatvalue="#{product.tags}"var="tag">

#{tag.name}<br/>

</ui:repeat>

</h:column>

</h:dataTable>

EDITABLE<H:DATATABLE>Astoformcomponentsnestedinside<h:column>,youcansubstituteELintemplatetextwithinputcomponentsasfollows:

<h:formid="list">

<h:dataTableid="products"value="#{products.list}"

var="product">

<h:column>

<f:facetname="header">ID</f:facet>

#{product.id}

</h:column>

8

Page 298: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:column>

<f:facetname="header">Name</f:facet>

<h:inputTextid="name"value="#{product.name}"/>

<h:messagefor="name"/>

</h:column>

<h:column>

<f:facetname="header">Description</f:facet>

<h:inputTextareaid="description"

value="#{product.description}">

</h:inputTextarea>

<h:messagefor="description"/>

</h:column>

</h:dataTable>

<h:commandButtonid="save"value="Save"action="#

{products.save}">

<f:ajaxexecute="@form"render="@form"/>

</h:commandButton>

</h:form>

Wherebythesave()methodofthebackingbeanclassbasicallylooksasfollows,afterhavingchangedthebackingbeanclasstobea@ViewScopedoneinsteadofa@RequestScopedone:publicvoidsave(){productService.update(products);

}

Andtheupdate()methodoftheserviceclassinturnlooksasfollows:@TransactionAttribute(REQUIRED)publicvoidupdate(Iterable<Product>products){

products.forEach(entityManager::merge);

}

Notethatyoudon’tneedtoworryatallaboutcollectingthesubmittedvalues.JSFhasalreadydonethattaskforyou.Alsonotethatyoudon’tneedtoworryaboutuniquenessofthe

Page 299: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

componentIDswithin<h:dataTable>,asthatcomponentalreadyimplementstheNamingContainerinterfaceandprependsitsownclientIDandtheiterationindextotheclientIDofthechildcomponents,asyoucanseeinthefollowinggeneratedHTMLoutput:

<tableid="list:products">

<thead>

<tr>

<thscope="col">ID</th>

<thscope="col">Name</th>

<thscope="col">Description</th>

</tr>

</thead>

<tbody>

<tr>

<td>3</td>

<td>

<inputid="list:products:0:name"type="text"

name="list:products:0:name"

value="Three">

</input>

</td>

<td>

<textareaid="list:products:0:description"

name="list:products:0:description"

>Thethirdproduct</textarea>

</td>

</tr>

<tr>

<td>2</td>

<td>

<inputid="list:products:1:name"type="text"

name="list:products:1:name"value="Two">

</input>

</td>

<td>

<textareaid="list:products:1:description"

Page 300: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

name="list:products:1:description"

>Thesecondproduct</textarea>

</td>

</tr>

<tr>

<td>1</td>

<td>

<inputid="list:products:2:name"type="text"

name="list:products:2:name"value="One">

</input>

</td>

<td>

<textareaid="list:products:2:description"

name="list:products:2:description"

>Thefirstproduct</textarea>

</td>

</tr>

</tbody>

</table>

Itmustbesaidthathavinganeditabletablelikethisisnotterriblyefficient,certainlynotwhenthetablecontainsalotofcolumnsandrows.JSFcanhandleitprettywell;onlytheaveragewebbrowserwillhaveahardtimehandlingit,certainlywhenthenumberofrowsexceedsafewthousand.AndthenI’mnotspeakingabouttheenduserpotentiallygoingcrazyfromscrollingthroughthewholepageallthetimeandbasicallyhavingnoclearoverview.Thereareseveralsolutionstothis:firstandforemostispagination;secondisfiltering;thirdisrow-basedinlineeditingandupdating;andfourthisexternaleditinginadialogordetailpage.

Allthementionedtable-specificperformanceandusabilitysolutionsarenotofferedbythestandard<h:dataTable>andthereforerequirequiteanamountofcustomcode.It’sstronglyrecommendedthatyoulookforanexistingJSF

Page 301: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

componentlibrarysupportingthesefeaturesinordertomakeyourJSFlifeeasierwithouttheneedtoreinventthewheel.CurrentlythemostwidelyusedoneisPrimeFaceswithits<p:dataTable>. Thiscanevenbefurthersimplifiedwith<op:dataTable>ofOptimusFaces, whichisinturnbasedon<p:dataTable>.EditinginadetailpageisonlydoablewithstandardJSF,andit’sdemonstratedinthe“CustomConverters”sectionofChapter5.

ItshouldbesaidthatinthespecificcaseofthepreviouslyshownList<String>example,turningthecolumnfromoutputtoinputisn’taseasilydoneaswiththeList<Product>example.Inotherwords,thefollowingexamplewon’tworkatall.

<h:form>

<h:dataTablevalue="#{bean.strings}"var="string">

<h:column>

<h:inputTextvalue="#{string}"/>

</h:column>

</h:dataTable>

<h:commandButtonvalue="Save"action="#{bean.save}">

<f:ajaxexecute="@form"/>

</h:commandButton>

</h:form>

Thetechnicalproblemisthatjava.lang.Stringisimmutableanddoesn’thaveapublicsettermethodforitsinternalvalue.True,itindeedalsodoesn’thaveagetter,butELalreadydefaultstoObject#toString()whichincaseofStringjustreturnstheverystringitself.Thiscanbesolvedbyreferencingthemodelvaluebyanindexasfollows:<h:form>

<h:dataTablebinding="#{table}"value="#

9

10

Page 302: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

{bean.strings}"var="string">

<h:column>

<h:inputTextvalue="#

{bean.strings[table.rowIndex]}"/>

</h:column>

</h:dataTable>

<h:commandButtonvalue="Save"action="#{bean.save}">

<f:ajaxexecute="@form"/>

</h:commandButton>

</h:form>

Notethebindingattribute.Basically,duringtheviewbuildtime,thissetsthecurrentUIComponentinstanceasanELvariableidentifiedbythegivenname.Inthisparticularsnippet,itwillthusmakethe#{table}variabletoreferencetheconcreteHtmlDataTableinstancebehindthe<h:dataTable>tag.The#{table}variableis,thenduringtheviewbuildtime,referenceableonlyafterthetaglocationintheviewandduringtheviewrendertimeanywhereintheview.Inthisway,youcanaccessitspropertiesasifitwereabean.#{table.rowIndex}basicallythusreferstotheUIData#getRowIndex()method, whichreturnsthecurrentiterationindex.And,finally,thisisusedtoreferencetheitemofinterestinthelist.Duringtheupdatemodelvaluesphase(fourthphase)JSFwillsimplyreplacetheitematthespecifiedindex.

Alsoforthebindingattributeit’sveryimportantthatthevariablenameshouldn’tclashwithexistingmanagedbeannamesorimplicitELobjectsandforsurenotwithothercomponentsinthesameview.Youcanalternativelyletthebindingattributereferenceabackingbeanpropertyas

11

Page 303: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

follows:<h:dataTablebinding="#{bean.table}"...>

With:

privateUIDatatable;//+getter+setter

Butthisisfairlyuselessifitisn’tusedanywhereelseinthebackingbean.Moreover,thisisdangerouswhenthemanagedbeanscopeiswiderthanrequest(seealsothesection“ViewBuildTime”inChapter3.Itisbetternottobindcomponentinstancestoabackingbeanatall;itmightindicateapoorpractice.Theonlyreasonablereal-worldusecaseinJSF2.xisbindingcompositecomponentchildrentoabackingcomponent(seealsothesection“CompositeComponents”inChapter7).

Incaseyou’reusing<ui:repeat>or<c:forEach>insteadof<h:dataTable>onsomethinglikeaList<String>,thenyoucanobtaintheiterationindexinamuchsimplerway,viathevarStatusattribute.

<h:form>

<ui:repeatvalue="#{bean.strings}"var="string"

varStatus="loop">

<h:inputTextvalue="#{bean.strings[loop.index]}">

<br>

</ui:repeat>

<h:commandButtonvalue="Save"action="#{bean.save}">

<f:ajaxexecute="@form"/>

</h:commandButton>

</h:form>

ADD/REMOVEROWSIN<H:DATATABLE>

Page 304: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Comingbacktothe<h:dataTable>withList<Product>,theremaybecasesinwhichyou’dliketobeabletoaddorremoveitemswhilestayinginthesameview,usuallyinsomesortofanadminpage.InordertoaddanewProduct,weneedtoprepareanewinstanceinthemanagedbean,fillitinaseparateform,persistit,andthenrefreshthetable.

<h:formid="list">

<h:dataTableid="products"value="#{products.list}"...>

...

</h:dataTable>

</h:form>

<h:formid="add">

<h:outputLabelfor="name"value="Name"/>

<h:inputTextid="name"value="#{products.product.name}"

/>

<h:messagefor="name"/>

<h:outputLabelfor="description"value="Description"/>

<h:inputTextareaid="description"

value="#{products.product.description}">

</h:inputTextarea>

<h:messagefor="description"/>

<h:commandButtonid="add"value="Add"action="#

{products.add}">

<f:ajaxexecute="@form"render="@form:list:products"

/>

</h:commandButton>

</h:form>

Wherebytherelevantbackingbeancodelooksasfollows:privateList<Product>list;//+getter

privateProductproduct=newProduct();//+getter

Page 305: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@PostConstruct

publicvoidinit(){

list=productService.list();

}

publicvoidadd(){

productService.create(product);

list.add(0,product);

product=newProduct();

}

Withthiscreate()methodinserviceclass:@TransactionAttribute(REQUIRED);

publicLongcreate(Productproduct){

entityManager.persist(product);

returnproduct.getId();

}

Removingcouldbedoneinseveralways.Inanycase,youmayneedanadditionalcolumntoholdthesubmitbuttonsorradiobuttonsorcheckboxes.Theeasiestwayisacolumnwithacommandbuttonwhichdeletesthecurrentlyiterateditemandthenrefreshesthetable.

<h:formid="list">

<h:dataTableid="products"value="#{products.list}"

var="product">

...

<h:column>

<h:commandButtonid="delete"value="Delete"

action="#{products.delete(product)}">

<f:ajaxrender="@namingcontainer"/>

</h:commandButton>

</h:column>

Page 306: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</h:dataTable>

</h:form>

Withthisdelete(Product)methodina@ViewScopedbackingbeanclass:publicvoiddelete(Productproduct){productService.delete(product);

list.remove(product);

}

Andthisdelete()methodinserviceclass:@TransactionAttribute(REQUIRED)

publicvoiddelete(Productproduct){

if(entityManager.contains(product)){

entityManager.remove(product);

}

else{

ProductmanagedProduct=

getById(product.getId());

if(managedProduct!=null){

entityManager.remove(managedProduct);

}

}

}

Notetherenderattributeof<f:ajax>.Itspecifies@namingcontainer,whichbasicallyreferencestheclosestparentNamingContainercomponent.FromthestandardJSFHTMLcomponentset,only<h:form>and<h:dataTable>areinstancesofNamingContainer.Inthisspecificconstruct,@namingcontainerthusreferencesthe<h:dataTable>.Youcouldalsohaveused<f:ajax

Page 307: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

render=":list:products">instead;it’sonlyslightlyverbose.The<f:ajaxrender="products">wouldn’twork,becauseitwilltrytofinditwithinthecontextofthecurrentlyiteratedrow,whichisbasicallywithinall<h:column>components.

SELECTROWSIN<H:DATATABLE>Havingaradiobuttoncolumnina<h:dataTable>isnativelypossiblesinceJSF2.3thankstothenewgroupattributeofthe<h:selectOneRadio>(seealsothesection“SelectionComponents”inChapter4).

<h:formid="list">

<h:dataTableid="products"value="#{products.list}"

var="product">

<h:column>

<h:selectOneRadioid="selected"group="selected"

value="#{products.selected}">

<f:selectItemitemValue="#{product}"/>

</h:selectOneRadio>

</h:column>

...

</h:dataTable>

<h:commandButtonid="deleteSelected"value="Delete

selectedproduct"

action="#{products.deleteSelected}">

<f:ajaxexecute="@form"render="products"/>

</h:commandButton>

</h:form>

WiththisdeleteSelected()methodina@ViewScopedbackingbeanclass:privateProductselected;//+getter+setter

Page 308: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicvoiddeleteSelected(){

productService.delete(selected);

list.remove(selected);

}

NotethatyouneedaProductConverterorBaseEntityConverterhereaswell.Thoseareelaboratedinthesection“CustomConverters”inChapter5.

Thecheckboxselectionisalittlemoreconvoluted.You’dintuitivelygrab<h:selectManyCheckbox>,butthisdoesn’tyetsupportthegroupattributeas<h:selectOneRadio>does.You’dneedtofallbackto<h:selectBooleanCheckbox>withaMap<Product,Boolean>wherebythemapkeyrepresentsthecurrentlyiteratedproductandthemapvaluerepresentsthecheckboxvalue.

<h:formid="list">

<h:dataTableid="products"value="#{products.list}"

var="product">

<h:column>

<h:selectBooleanCheckboxid="selection"

value="#{products.selection[product]}">

</h:selectBooleanCheckbox>

</h:column>

...

</h:dataTable>

<h:commandButtonid="deleteSelected"value="Delete

selectedproducts"

action="#{products.deleteSelected}">

<f:ajaxexecute="@form"render="products"/>

</h:commandButton>

</h:form>

Page 309: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ThemodifieddeleteSelected()methodinthe@ViewScopedbackingbeanlooksasfollows:privateMap<Product,Boolean>selection=newHashMap<>();//

+getter

publicvoiddeleteSelected(){

List<Product>selected=

selection.entrySet().stream()

.filter(Entry::getValue)

.map(Entry::getKey)

.collect(Collectors.toList());

productService.delete(selected);

selected.forEach(list::remove);

selection.clear();

}

TheoverloadedProductService#delete(Iterable)methodlooksasfollows:@TransactionAttribute(REQUIRED)publicvoiddelete(Iterable<Product>products){

products.forEach(this::delete);

}

DYNAMICCOLUMNSIN<H:DATATABLE>With<h:dataTable>,withthehelpofJSTL<c:forEach>,itisalsopossibletodynamicallycreatemultiple<h:column>instancesbasedonaJavamodelwhichisatleastviewscoped.Arequest-scopedmodelcanalso,butthisdoesn’tguaranteethatduringapostbackrequestitisexactlythesameasitwasduringtheprecedingrequest,

Page 310: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

andthereforethereisariskofthedynamic<h:column>compositionbeingoff.

Thevalueofthe<c:forEach>shouldreferenceacollectionincludingatleasttheentitypropertynamesorevenmapkeys.YoucanthenusethebracenotationinELasin#{entity[propertyName]}or#{map[key]}toreferencetheactualvalue.ThisworksforbothUIOutputandUIInputcomponents.ThefollowingexampleillustrateshowyoucouldachievethisforaList<Product>.

Backingbean:

@Named@RequestScoped

publicclassProducts{

privateList<Product>list;

privateList<String>properties;

@Inject

privateProductServiceproductService;

@PostConstruct

publicvoidinit(){

list=productService.list();

properties=Arrays.asList("id","name",

"description");

}

//Add/generategetters(settersnotneededhere).

}

Faceletsfile:

<h:dataTablevalue="#{products.list}"var="product">

<c:forEachitems="#{products.properties}"var="property">

<h:column>#{product[property]}</h:column>

Page 311: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</c:forEach>

</h:dataTable>

Youcouldevengeneralizethisfurtherforotherentitiesofacommonsuperclass,suchasBaseEntity,wherebyyouobtaintherelevantpropertynamesfromtheentityservice.

ResourceComponentsJSFoffersthreeresourcecomponents,<h:graphicImage>,<h:outputScript>,and<h:outputStylesheet>,forimageresources,JavaScriptresources,andCSSresources,respectively.Theycanreferencephysicalresourcefilesaswellasdynamicresourcefiles.Thephysicalresourcefilesthemselvesmustbeplacedinthe/resourcessubfolderofthemainwebfolder.ThedynamicresourcefilescanbehandledwithacustomResourceHandlerwhichinterceptsonaspecificlibrarynameand/orresourcename.GiventhefollowingfolderstructureinaMavenWARprojectinEclipse,

Page 312: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

theresourcesarereferenceableasfollows:

<h:graphicImagename="images/some.svg"/>

<h:outputScriptname="scripts/some.js"/>

<h:outputStylesheetname="styles/some.css"/>

ThegeneratedHTMLoutputlooksasfollows,providedthat/projectisthecontextpathofthewebapplication:

<imgsrc="projectjavax.faces.resource/images/some.svg.xhtml"

/>

<scripttype="text/javascript"

src="projectjavax.faces.resource/scripts/some.js.xhtml">

</script>

<linktype="text/css"rel="stylesheet"

href="projectjavax.faces.resource/styles/some.css.xhtml"

/>

You’llseethatit’sprefixedwith/javax.faces.resourcepathandsuffixedwiththecurrentlyactiveURLpatternoftheFacesServlet.The/javax.faces.resourceisrepresentedbytheconstantResourceHandler#RESOURCE_IDENTIFIER. ThattheresourceURLmatchestheURLpatternoftheFacesServletensuresthatitwillactuallyinvoketheFacesServletwhichinturnknowshowtohandletheresource.ItwillfirstinvokeResourceHandler#isResourceRequest(),whichbydefaultdeterminesiftheURLprefixstartswiththeknownRESOURCE_IDENTIFIERconstant,andifsothendelegatestoResourceHandler#handleResourceRequest()insteadofgoingthroughtheJSFlifecycle.

Alsonotethatthewebresourcesarenotplacedinthe

12

Page 313: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

src/main/resourcesfolderbutinthesrc/main/webapp/resourcesfolder.Thesrc/main/resourcesfolderisonlyfornon-classresourceswhichmustendupintheclasspath,suchasresourcebundlefiles.TheseclasspathresourcesarethenobtainablebyClassLoader#getResource().src/main/webapp/resourcesdoesn’tendupintheclasspath;instead,itendsupinthewebcontent.ThesewebresourcesarethenobtainablebyExternalContext#getResource(), whichdelegatesunderthehoodtoServletContext#getResource().

Thenameattributethusbasicallyrepresentsthepathtotheresourcerelativetothesrc/main/webapp/resourcesfolder.Thesecomponentsalsosupportalibraryattribute.ThelibraryattributemustrepresenttheuniqueresourcelibrarynameofaJSFlibrary.ForstandardJSF,theresourcelibrarynameis“javax.faces”,forPrimeFaces, theresourcelibrarynameis“primefaces”,forOmniFaces,theresourcelibrarynameis“omnifaces”,forBootsFaces, theresourcelibrarynameis“bsf”,andsoon.Normally,theselibrary-specificresourcesarealreadyautomaticallyincludedbytheJSFlibraryinquestion,usuallydeclarativelyviathe@ResourceDependencyannotationontheUIComponentorRendererclass,andsometimesprogrammaticallyviaUIViewRoot#addComponentResource().Thisiselaboratedinthesection“ResourceDependencies”inChapter11.Thoseresourcescanifnecessarybereferencedusingaresourcecomponentwherebyyouthusexplicitlyspecifythelibraryattribute.

13

14

15

16

17

18

Page 314: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ThefollowingexampleexplicitlyincludesthestandardJSFjsf.jsfile:<h:head>...

<h:outputScriptlibrary="javax.faces"name="jsf.js"

/>

</h:head>

ThisisusuallyunnecessaryastheJSFcomponentsdependingonthisscript,suchas<h:commandLink>,<f:ajax>,and<f:websocket>alreadyautomaticallyincludeit.Here’sanotherexamplewhichexplicitlyincludesthejquery.jsfilefromPrimeFaceslibrary—thisworksofcourseonlyifyouhavePrimeFacesinstalled.

<h:head>

...

<h:outputScriptlibrary="primefaces"

name="jquery/jquery.js"/>

</h:head>

Thiscanbeusefulwhenyou’dliketoreusethePrimeFaces-providedjQuerylibraryonapagethatdoesn’tnecessarilycontainPrimeFacescomponents.Thatis,thisscriptwon’tbeautomaticallyincludedwhenthepagedoesn’tcontainanyPrimeFacescomponent,butyoumighthappentohavesomewebproject-specificscriptswhichinturndependonjQuery.JSFresourcemanagementwillalreadymakesurethatbothautomaticallyincludedandexplicitlyincludedJavaScriptandCSSresourcesdon’tgetduplicatedinthegeneratedHTMLoutput.Inotherwords,theabovelinewhichexplicitlyincludesjQuerycansafelybeusedonapagethatdoescontainaPrimeFacescomponent.

Page 315: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Notethattherearecurrentlyarelativelylargenumberofpoor-qualityJSFtutorialsontheInternetwhichdon’tcorrectlyusethelibraryattribute.Instead,thosetutorialsincorrectlydemonstratethelibraryattributetorepresentthesubfolderwithinthesrc/main/webapp/resourcesfolder—somethinglikethefollowing:<h:graphicImagelibrary="images"name="some.svg"/>

<h:outputScriptlibrary="scripts"name="some.js"/>

<h:outputStylesheetlibrary="styles"name="some.css"/>

Thisisoutrightwrong.Itdoesn’tofferanycustomresourcehandlertodistinguishlibrary-specificresourcesfromeachother.Intheaboveexample,you’dbasicallyneedtocheckthreedifferentresourcelibrarieseventhoughallthoseresourcesbelongtotheverysamelibrary—thewebprojectitself.

Talkingaboutcustomresourcehandlers,imaginethatyouwanttocompelthewebbrowsertoforciblyreloadtheimage,JavaScript,and/orCSSresourcewhenithaschangedintheserversideinstead.ThiscanbeachievedbyaddingaquerystringparametertotheresourceURLwhosevaluerepresentstheversionoftheresource.Thisisalsocalled“cachebusting.”InJSF,thiscanbeachievedwithacustomResourceHandlerwhichdecoratestheResourcetoreturnitslastmodifiedtimestampasaquerystringparameter.

publicclassVersionResourceHandlerextends

ResourceHandlerWrapper{

publicVersionResourceHandler(ResourceHandlerwrapped){

super(wrapped);

}

Page 316: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Override

publicResourcecreateResource(Stringname,String

library){

Resourceresource=super.createResource(name,

library);

if(resource==null||library!=null){

returnresource;

}

returnnewResourceWrapper(resource){

@Override

publicStringgetRequestPath(){

Stringurl=super.getRequestPath();

returnurl

+(url.contains("?")?"&":"?")

+"v="+getLastModified();

}

privatelonggetLastModified(){

try{

returngetWrapped().getURL()

.openConnection().getLastModified();

}

catch(IOExceptionignore){

return0;

}

}

};

}

}

Inordertoactivateit,registeritinthefaces-config.xmlasfollows:<application><resourcehandler>

com.example.project.resourcehandler.VersionResou

rceHandler

Page 317: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</resourcehandler>

</application>

NotethatthecreateResource()methodreturnsthecreatedresourceunmodifiedwhenit’snullorwhenthelibraryisnotnull.Theresourceitselfisnullwhenthenameisunknown.Thelibraryisnullwhenit’sunspecifiedandthusspecifictothewebproject.Youcouldofcoursealsoapplythislogictoallresourcesofotherlibraries,buttheyusuallyalreadyhavetheirownversionofaresourcehandler.

Comingbacktotheresourcecomponents,youcanplace<h:graphicImage>onlyinsidethebody,whichcanbeboththeplainHTML<body>ortheJSF<h:body>.Obviously,inanHTMLdocument,youcanhavean<img>elementonlyinsidethedocumentbody.Youcanplace<h:outputScript>and<h:outputStylesheet>basicallyanywhereintheJSFpage.The<h:outputScript>willbydefaultgeneratetheHTML<script>elementatexactlythedeclaredlocation,regardlessofbeingintheheadorthebodyofthedocument.<h:outputStylesheet>,however,willbydefaultbemovedtotheendof<h:head>whendeclaredinside<h:body>.Thatis,inHTMLit’sillegaltohavea<linkrel="stylesheet">outside<head>.The<h:outputScript>canalsobeautomaticallymovedtoendofdocumentheadwhendeclaredinside<h:body>withthetargetattributesettohead.Whenthetargetattributeof<h:outputScript>issettobody,thenitwillbeautomaticallymovedtoendofthedocumentbody.

Page 318: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:outputStylesheet>doesn’tsupportit.Inotherwords,thefollowingtestFacelet,<h:head><title>Resourcecomponentrelocationdemo.</title>

<h:outputStylesheetname="style1.css"/>

<h:outputScriptname="script1.js"/>

<h:outputScriptname="script2.js"target="head"/>

<h:outputScriptname="script3.js"target="body"/>

</h:head>

<h:body>

<p>Paragraph1</p>

<h:outputStylesheetname="style2.css"/>

<h:outputScriptname="script4.js"/>

<h:outputScriptname="script5.js"target="head"/>

<h:outputScriptname="script6.js"target="body"/>

<p>Paragraph2</p>

</h:body>

willbasicallygeneratethefollowingHTMLoutput(URLssimplifiedforbrevity).

<head>

<title>Resourcecomponentrelocationdemo.</title>

<scripttype="text/javascript"src="script1.js"></script>

<linktype="text/css"rel="stylesheet"href="style1.css"

/>

<scripttype="text/javascript"src="script2.js"></script>

<linktype="text/css"rel="stylesheet"href="style2.css"

/>

<scripttype="text/javascript"src="script5.js"></script>

</head>

<body>

<p>Paragraph1</p>

<scripttype="text/javascript"src="script4.js"></script>

<p>Paragraph2</p>

Page 319: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<scripttype="text/javascript"src="script3.js"></script>

<scripttype="text/javascript"src="script6.js"></script>

</body>

Inotherwords,theresourcerenderingorderinthedocumentheadis:

1. 1. <h:outputScript>from<h:head>withouttarget.

2. 2. <h:outputStylesheet>from<h:head>.

3. 3. <h:outputScript>from<h:head>withtarget="head".

4. 4. <h:outputStylesheet>from<h:body>.

5. 5. <h:outputScript>from<h:body>withtarget="head".

Notethat<h:outputStylesheet>implicitlyinferstarget="head"andisthereforerenderedafter<h:outputScript>withoutanytarget.AllJavaScriptandCSSresourceswhichareautomaticallyincludedviathe@ResourceDependencyannotationofcomponentswillendupbetweentheresourcesdeclaredin<h:head>andthosedeclaredin<h:body>.SoifyouhappentouseaJSFlibrarywhichautomaticallyincludesabunchofCSSresources,andyou’dliketooverridesomeofthem,you’dbestputsuch<h:outputStylesheet>in<h:body>sothatyoucanguaranteethatit’sloadedafterthelibrary’s.

Bewarethough,someJSFlibrarieswillautomaticallyoverridethedefaultrendererof<h:head>whichmaymessupthedefaultresourceordering.Insuchacase,you’dbestconsultthedocumentationoftheJSFlibraryinquestionforneworderingrules,ortorestorethedefaultrendererof<h:head>viathewebproject’sfaces-config.xml.

<renderkit>

Page 320: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<renderer>

<component-family>javax.faces.Output</component-

family>

<renderer-type>javax.faces.Head</renderer-type>

<renderer-class>

com.sun.faces.renderkit.html_basic.HeadRenderer

</renderer-class>

</renderer>

</renderkit>

Incaseyou’reusingMyFacesinsteadofMojarraasJSFimplementation,useorg.apache.myfaces.renderkit.html.HtmlHead

Rendererinsteadastherendererclass.IncaseyouintendtodevelopsuchaJSFlibrarywhich

automaticallyincludesspecificresources,keepinmindtouse@ResourceDependencyorUIViewRoot#addComponentResource()insteadofreplacingthedefaultrendererof<h:head>forthepurpose.Asannotationsdon’tallowspecifyingdynamicvalues,anydynamicresourcescanbestbeaddedduringthePostAddToVieweventof<h:head>.Thiscanbeachievedapplication-widewithaSystemEventListenerasfollowsassumingthattheJSFlibrary’sresourcelibrarynameis“foo”:publicclassDynamicResourceListenerimplementsSystemEventListener{

privatestaticfinalStringLIBRARY="foo";

@Override

publicbooleanisListenerForSource(Objectsource){

UIOutputoutput=(UIOutput)source;

Page 321: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

return

"javax.faces.Head".equals(output.getRendererType());

}

@Override

publicvoidprocessEvent(SystemEventevent){

FacesContextcontext=event.getFacesContext();

StringscriptName="foo.js";//Canbedynamic.

addResource(context,scriptName);

StringstylesheetName="foo.css";//Canbe

dynamic.

addResource(context,stylesheetName);

}

privatevoidaddResource(FacesContextcontext,

Stringname){

UIComponentresource=newUIOutput();

resource.getAttributes().put("library",

LIBRARY);

resource.getAttributes().put("name",name);

resource.setRendererType(context.getApplication(

)

.getResourceHandler().getRendererTypeForReso

urceName(name));

context.getViewRoot()

.addComponentResource(context,resource,

"head");

}

}

Page 322: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

whichisregisteredinfaces-config.xmlasfollows:<system-event-listener>

<system-event-listener-class>

com.example.project.listener.DynamicResourceList

ener

</system-event-listener-class>

<system-event-class>

javax.faces.event.PostAddToViewEvent

</system-event-class>

<source-

class>javax.faces.component.UIOutput</source-class>

</system-event-listener>

Notethat<source-class>couldbetterhavebeenajavax.faces.component.html.HtmlHead,butthisdoesn’tnecessarilyworkacrossallJSFimplementations.In,forexample,Mojarra,<h:head>implicitlycreatesaninstanceofUIOutputinsteadofHtmlHead.

Onceinstalled,thisDynamicResourceListenerwillresultinthefollowingHTMLoutputforexactlythelastshowntestFaceletwithstyle1.css,script1.js,script2.js,etc.(alsohere,URLsaresimplifiedforbrevity).

<head>

<title>Resourcecomponentrelocationdemo.</title>

<scripttype="text/javascript"src="script1.js"></script>

<scripttype="text/javascript"src="foo.js"></script>

<linktype="text/css"rel="stylesheet"href="foo.css"/>

<linktype="text/css"rel="stylesheet"href="style1.css"

/>

<scripttype="text/javascript"src="script2.js"></script>

<linktype="text/css"rel="stylesheet"href="style2.css"

Page 323: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

/>

<scripttype="text/javascript"src="script5.js"></script>

</head>

<body>

<p>Paragraph1</p>

<scripttype="text/javascript"src="script4.js"></script>

<p>Paragraph2</p>

<scripttype="text/javascript"src="script3.js"></script>

<scripttype="text/javascript"src="script6.js"></script>

</body>

Theresourcerenderingorderinthedocumentheadisthus1. 6. <h:outputScript>from<h:head>withouttarget.

2. 7. DynamicscriptaddedtoheadduringPostAddToView.

3. 8. DynamicstylesheetaddedtoheadduringPostAddToView.

4. 9. <h:outputStylesheet>from<h:head>.

5. 10. <h:outputScript>from<h:head>withtarget="head".

6. 11. <h:outputStylesheet>from<h:body>.

7. 12. <h:outputScript>from<h:body>withtarget="head".

Yousee,theorderingisquitepredictable.Itshouldn’thavebeennecessarytooverridetherendererof<h:head>.Moreover,overridingtherendererof<h:head>fromaJSFlibraryrisksthepossibilitythatitbecomesincompatiblewithanyotherJSFlibrarywhichcoincidentallyalsooverridestherendererof<h:head>.You’dreallywanttoavoidthat.

AnotheradvantageofusingresourcecomponentsisthatJSFwillautomaticallypushallresourcesassociatedwiththedocumenttotheclient,sothattheclientwillbeabletoretrievethemsoonerthanthetimeneededtoparsetheHTMLdocumentandlocateall<link>,<script>,and<img>elements.ThisisnewsinceJSF2.3.Thisonlyrequiresthatthe

Page 324: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Pass-ThroughElementsJSFalsosupportsimplicitlyinterpretinganyarbitraryHTMLelementasafull-fledgedJSFcomponent.ThisfeaturewasintroducedinJSF2.2andisformallyknownas“pass-throughelements.”ThisisparticularlyusefulwhenyouwanttouseHTML5elementssuchas<main>,<article>,<section>,<aside>,<nav>,<header>,<footer>,etc.andwanttobeabletoreferencethemin<f:ajaxrender>.Previously,beforeJSF2.2,thoseelementsdidn’thaveaJSFcomponentequivalentandyou’rethereforeforcedtowrapthemin<h:panelGrouplayout="block">whichonlymakestheHTMLlesssemantic.Thepass-throughelementtriggerisavailablebythehttp://xmlns.jcp.org/jsfnamespace.Allyouneedtodoistospecifyatleastoneattributeonthisnamespace.Thedefaultnamespaceprefixisjust“jsf”.

<!DOCTYPEhtml>

<htmllang="en"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:jsf="http://xmlns.jcp.org/jsf"

xmlns:h="http://xmlns.jcp.org/jsf/html"

>

<h:head>

<title>Title</title>

</h:head>

<h:body>

<header>

...

JSF2.3webapplicationisdeployedtoaServlet4.0-compatiblecontainer(Payara5,GlassFish5,Tomcat9,WildFly12,etc.),andthatHTTPSisbeingusedinsteadofHTTP,andthattheclientsupportsHTTP/2protocol. ThisdoesnotrequireadditionalconfigurationfromtheJSFsideon.

19

Page 325: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<navjsf:id="menu">

...

</nav>

</header>

<mainjsf:id="main">

...

</main>

<footer>

...

</footer>

</h:body>

</html>

Underthehood,intheJSFcomponenttree,thoseHTML5elementsareturnedintoaUIPanelcomponentandaretreatedintheJSFcomponenttreeexactlylike<h:panelGroup>.ThiswayyoucancleanlykeepusingsemanticHTML5markupwhilestillbeingabletoreferencethemvia<f:ajaxrender>.Inotherwords,thefollowingconstructwon’twork:<mainid="main">

...

<h:formid="form">

...

<h:commandButtonid="submit"...>

<f:ajaxrender=":main"/>

</h:commandButton>

</h:form>

</main>

ItfailsbecauseUIViewRoot#findComponent()doesn’treturnanythingwhenpassing“main”.JSFcan’tfindanycomponentwiththegivenID.The<main>elementisherebasicallyinterpretedastemplatetext.Butthefollowingconstructwillwork:<mainjsf:id="main">...

Page 326: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:formid="form">

...

<h:commandButtonid="submit"...>

<f:ajaxrender=":main"/>

</h:commandButton>

</h:form>

</main>

UIViewRoot#findComponent()on“main”willthenreturnaUIPanelinstancerepresentingthe<main>element.JSFwillthenbeabletorenderitintotheAjaxresponse.Thepass-throughelementfeaturealsoworksonotherHTMLelements,onlytheydon’tnecessarilyturnintoaUIPanelinstance.Instead,theywillbeturnedintoaJSFcomponentwhosegeneratedHTMLoutputmatchestheveryHTMLelement(seeTable6-1).Thefollowingconstructis,underthehood,identicaltothepreviousone:<mainjsf:id="main">...

<formjsf:id="form">

...

<inputtype="submit"jsf:id="submit"...>

<f:ajaxrender=":main"/>

</input>

</form>

</main>

Table6-1 PassthroughElementsRecognizedbyJSF

PassthroughHTMLelement

ImpliedJSFcomponent

<ajsf:action="…">

<h:commandLink>

<ajsf:actionListener="…">

<h:commandLink>

Page 327: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<ajsf:actionListener="…">

<h:commandLink>

<ajsf:value="…">

<h:outputLink>

<ajsf:outcome="…">

<h:link>

<bodyjsf:id="…">

<h:body>

<buttonjsf:id="…">

<h:commandButtontype="button">

<buttonjsf:outcome="…">

<h:button>

<formjsf:id="…">

<h:form>

<headjsf:id="…">

<h:head>

<imgjsf:id="…">

<h:graphicImage>

<inputjsf:id="…"type="button">

<h:commandButtontype="button">

<inputjsf:id="…"type="checkbox">

<h:selectBooleanCheckbox>

Page 328: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<inputjsf:id="…"type="file">

<h:inputFile>

<inputjsf:id="…"type="hidden">

<h:inputHidden>

<inputjsf:id="…"type="password">

<h:inputSecret>

<inputjsf:id="…"type="reset">

<h:commandButtontype="reset">

<inputjsf:id="…"type="submit">

<h:commandButtontype="submit">

<inputjsf:id="…"type="*">

<h:inputText>

<labeljsf:id="…">

<h:outputLabel>

<linkjsf:id="…">

<h:outputStylesheet>

<scriptjsf:id="…">

<h:outputScript>

<selectjsf:id="…">

<h:selectOneListbox>

<selectjsf:id="…"multiple="*">

<h:selectManyListbox>

Page 329: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<*jsf:id="…">

<h:panelGroup>

Anyattributespecifiedonsuchapass-throughelementisimplicitlymappedtothecorrespondingattributeoftheJSFcomponent.Inthefollowingexample,theJSFcomponentandpass-throughelementpairsareequivalent.

<h:graphicImagelibrary="common"name="some.svg"/>

<imgjsf:library="common"name="some.svg"/>

<h:inputTextvalue="#{bean.name}"/>

<inputtype="text"jsf:value="#{bean.name}"/>

<h:inputTexta:type="email"value="#{bean.email}"/>

<inputtype="email"jsf:value="#{bean.email}"/>

<h:linkoutcome="contact"value="Contact"/>

<ajsf:outcome="contact">Contact</a>

Notethatyoudon’tnecessarilyneedtoregistereverysingleattributeofapass-throughelementonthe“jsf”namespace.Onlyoneissufficienttotriggerthepass-throughelementfeature,preferablythefirstone.Thiskeepsthecodeconcise.

Footnotes1https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/UIViewRoot.html#addComponentResource

-javax.faces.context.FacesContext-javax.faces.component.UIComponent-

java.lang.String-.

2https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/UIViewRoot.html#getComponentResource

s-javax.faces.context.FacesContext-java.lang.String-.

3https://github.com/atlassian/commonmark-java.

Page 330: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

4https://docs.oracle.com/javase/8/docs/api/java/text/MessageFormat.html.

5https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements.

6https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements.

7https://javaee.github.io/javaee-

spec/javadocs/javax/faces/context/ExternalContext.html#getRequestHeaderM

ap--.

8

https://javaserverfaces.github.io/docs/2.3/vdldocs/facelets/h/dataTable.

html.

9https://www.primefaces.org/showcase/ui/data/datatable/basic.xhtml.

10https://github.com/omnifaces/optimusfaces.

11https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/UIData.html#getRowIndex--.

12https://javaee.github.io/javaee-

spec/javadocs/javax/faces/application/ResourceHandler.html#RESOURCE_IDEN

TIFIER.

13

https://docs.oracle.com/javase/8/docs/api/java/lang/ClassLoader.html#get

Resource-java.lang.String-.

14https://javaee.github.io/javaee-

spec/javadocs/javax/faces/context/ExternalContext.html#getResource-

java.lang.String-.

15https://javaee.github.io/javaee-

spec/javadocs/javax/servlet/ServletContext.html#getResource-

java.lang.String-.

16http://www.primefaces.org.

Page 331: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

17http://omnifaces.org.

18http://bootsfaces.net.

19https://caniuse.com/#feat=http2.

Page 332: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1) (2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_7

7.FaceletsTemplating

BaukeScholtz andArjanTijms

Willemstad,Curaçao Amsterdam,Noord-Holland,TheNetherlands

WhenJSF(JavaServerFaces)cameoutforfirsttimein2004,onlyJSP(JavaServerPages)hadviewtechnology.Itwasimmediatelyclearthatitwasanill-suitedviewtechnologyforwebdevelopmentwithJSF.TheproblemwithJSPisthatitwritestotheHTTPresponseassoonasitencounterstemplatetext,whileJSFwouldliketofirstcreateacomponenttreebasedontheviewdeclarationinordertobeabletoperformthelifecycleprocessingonit.Forexample,thefollowingJSF1.0/1.1pageusingJSPviewtechnology,<h:commandLinkaction="...">

<strong>stronglinktext</strong>

</h:commandLink>

wouldproducethefollowingHTMLoutput,withtemplatetextemittedbyJSPbeforethegeneratedHTMLoutputofJSFcomponents :<strong>stronglinktext</strong><ahref="..."onclick="..."></a>

The“correctapproach”wouldbetowraptemplatetextin

1 2

1

Page 333: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:verbatim>tags,<h:commandLinkaction="..."><f:verbatim><strong>stronglinktext</strong>

<f:verbatim>

</h:commandLink>

whichwouldproducethefollowingHTMLoutput:<ahref="..."onclick="..."><strong>stronglink

text</strong></a>

Thishasobviouslyreceivedalotofcriticism,andthegeneralrecommendationnowisthatpeopleshouldnotbemixingJSFwithHTML.AnotherproblemwithJSF1.0/1.1wasthattherewasnocomponenttorepresentanHTML<div>.Thatwasaroundthetime“web2.0”hadjuststarted,andpeoplealsostartedtodiscommendtheuseofHTMLtablestolayoutawebpage.Theprevailingviewwasthatonlydivsshouldbeused,whichmadepeopledislikeJSF1.0/1.1evenmore.

ThepeculiarJSPbehaviorofcausingadisorganizedHTMLoutputwasworkedaroundinJSF1.2whichwasreleasedonlytwoyearslaterin2006,andthemissingcomponenttorepresentanHTML<div>wassolvedbygivingthe<h:panelGroup>componentanewlayout="block"attributesothatitrendersa<div>insteadof<span>.So,already,sinceJSF1.2,peoplecansafelymixplainHTMLwithJSFcomponentsinaJSPpageandcontinueusingdivsinbothplainHTMLandJSFways.However,therecommendedplantoavoidplainHTMLwhileauthoringJSF1.0/1.1pagesturnedintoapersistentmythwhichiseventodaystillaliveamongsomepeople.

AnotherproblemwithJSPisthatexistingJSPtaglibssuchasJSTL(JSPStandardTagLibrary)andexistingJSP

Page 334: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

expressionsintheformof${…}didn’tintegrateatallintotheJSFlifecycleandthereforemixingexistingJSPtaglibsandJSPexpressionswithJSFcomponentsinaJSPpagewouldresultinconfusingandunintuitivebehavior.Peoplecouldn’tuse<c:forEach>torenderalistofJSFcomponentsasthosecomponentswouldn’tseethevariabledeclaredby<c:forEachvar>.TherewasnodedicatedJSFcomponenttoloopoveralistotherthan<h:dataTable>andultimatelypeoplearestuckwithtableswhilecreatinglistsintheJSFpage.

Finally,JSPalsooffersverylimitedtemplatingcapabilitieswithactuallyonlyone“templating”tag,<jsp:include>;therefore,templatingwithJSPwouldrequireafairlycomplicatedapproachofcreatingabunchofcustomtagsforeverydefinitionofareusabletemplatesection. ThiscontradictsthephilosophyofJSF,tohavereusablecomponentstominimizecodeduplication.WithJSP,youwouldendupduplicatingJSFcomponentsthemselves.ExistingtemplatingframeworkssuchasTilesandThymeleafareeitherJSPcentricordon’tsupportJSFatallandthuscannotbeused.

Therewasclearlyastrongneedforanew-JSForientedviewtechnologywhichshouldsupplantJSPandsolveallofitsproblemswiththeJSFlifecycle.AndthenFaceletswasintroducedin2006.ItcouldbeinstalledseparatelyforJSF1.1and1.2,anditshippedbuilt-inwithJSF2.0.ItbecamethedefaultviewtechnologyforJSFandJSPwasdeprecatedasviewtechnologyforJSF.NewtagsintroducedinJSF2.0,suchas<f:ajax>,<h:head>,<h:body>,<h:outputScript>,and<h:outputStylesheet>,

2

Page 335: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

areonlyavailableforFaceletsandnotforJSP.JSF2.0alsointroducedanewinterfacetomoreeasilyplugacustomviewdeclarationlanguage(VDL)asalternativetoJSPandevenFacelets,theViewDeclarationLanguageAPI.Thisway,onecouldcreate,forexample,apureJava-basedVDLforJSF.

XHTMLTheFaceletsVDLspecifiesthattheviewsaredefinedinXML-basedfileswhicharecompiledusingaSAXparserandkeptaroundinmemory.Thismemorycacheis,sinceJSF2.1,configurableusingacustomFaceletCacheFactory.WhentheJSFprojectstageissettoDevelopment,theSAX-compiledrepresentationsofFaceletsfilesarebydefaultnotcached.ThisallowseasierdevelopmentagainstanalreadyrunningserverbyjusteditingtheFaceletsfilesfrominsideanIDE(integrateddevelopmentenvironment)whichsupportshot-publishingthelocalchangestothetargetruntime.

TheFaceletsfilesthemselvesusuallyusethe.xhtmlextensionandthereforestartersoftencallthem“XHTML”insteadof“Facelets.”ThisisokaywhentalkinginthecontextofJSF,buttheterm“XHTML”hasanothersideinthewebdevelopmentworld.Atitscore,XHTMLisamarkuplanguageforHTMLpageswhichneedtobecompiledusinganXML-basedtool.Inotherwords,developersbasicallycreateXMLfileswithHTMLmarkupmixedwithwebframework-specificXMLtags,afterwhichthewebframeworkwillparsethemintoanXMLtree,generatesomewebframework-specificrepresentationoftheXMLtree(whichisinJSFtheUIViewRoot),andultimatelygeneratethedesiredHTML

3

Page 336: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

outputbasedontheframework’sinternalrepresentationoftheXMLtree.

ButaroundthetimeofFacelets’introductionin2006,XHTMLwasbeingoverhypedbyanothergroupofwebdeveloperswhowerebasicallydisappointedbyseeingtheW3validatorinvalidatetheirHTML4documents—generallybecauseofthedesiretoexplicitlyclosealltagsforconsistency,includingthosewhichshouldactuallynotbeclosedasperHTML4specification,suchas<link>,<meta>,<br>,and<hr>—andsometimesbecauseofthedesiretospecifycustomtagattributesinordertohavesomeJavaScriptplug-instointeractmorecleanlywiththeHTMLdocument,whichisalsodisallowedbytheHTML4specification.

Eventhoughbasicallyeverysinglewebbrowserintheworldlenientlyacceptedthat,includingJurassicIE6,thosedevelopersdidn’twanttoseetheircarefullycraftedHTML4documentsbeinginvalidatedbytheW3validatorandchangedtheirHTMLdoctypedeclarationtousetheXHTMLDTD,anextensiontoHTMLwhichrequireseverytagtobeclosedandallowscustomattributestobespecifiedonexistingtags.However,thisisessentiallyabuseofXHTMLastheydidn’tatallactuallyuseanyXMLtooltocompilethedocumentandgeneratethedesiredHTMLoutput.ItwasmerelytokeeptheW3validatorhappy.

Coincidentally,alsoaroundthattime,HTML5wasjuststartedindraft.Essentially,developerscouldjuststripoutanyDTDfromtheHTMLdoctypedeclarationinordertogetanHTMLdocumentwhichallowsXML-basedsyntaxwhereinalltagsarealwaysclosedandcustomelementsandattributesare

Page 337: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

allowedand,importantly,validatedcorrectlyintheW3validator.Inotherwords,<!DOCTYPEhtml>wasbeensufficientforthosedevelopers,eveninIE6.Unfortunately,ittookagesbeforeHTML5wasofficiallyfinished,sodeveloperskeptabusingtheXHTMLdoctypebeforetheycouldswitchbacktotheHTMLdoctype.WhentalkingaboutFaceletstothosegroupofdevelopers,don’tcallit“XHTML”butjust“Facelets”;otherwiseitwillgenerateconfusion.

TemplateCompositionsFaceletsprovidestagsforeasilycreatingtemplatecompositionsbasedonasinglemastertemplatefile.Thisshouldreducecodeduplicationforsite-widesectionswhicharerepeatedacrossallwebpages,suchasheader,navigationmenu,andfooter.Themastertemplatefileshouldrepresentafull-blownwebpagelayoutwithallsite-widesectionsanduse<ui:insert>tagstorepresentplaceswherethepage-specificsectionscanbeinserted.Followingisabasicexampleofsuchamastertemplatefile,WEB-INFtemplates/layout.xhtml:<!DOCTYPEhtml><htmllang="en"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

>

<h:head>

<title>#{title}</title>

</h:head>

<h:body>

<header>

Page 338: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<ui:includesrc="WEB-

INFincludes/layout/header.xhtml"/>

</header>

<main>

<ui:insertname="content"/>

</main>

<footer>

<ui:includesrc="WEB-

INFincludes/layout/footer.xhtml"/>

</footer>

</h:body>

</html>

Notethatthemastertemplatefileandincludefilesareexplicitlyplacedinthe/WEB-INFfolder.ThisisdoneinordertopreventdirectaccessbyuserswhoareguessingthepathinURL.AlsonotethatthepagetitleisdeclaredasasimpleEL(ExpressionLanguage)expression#{title}insteadof<ui:insert>.Itisnotallowedtohaveanymarkupinthe<title>element.

Thexmlnsattributebasicallydefines,viaaURI(uniformresourceidentifier),whichtagscanbeusedinthedeclaredXMLnamespace.TherootXMLnamespacespecifiestheURIoftheW3XHTMLstandardhttp://www.w3.org/1999/xhtmlwhichthusdefines“anyXHTMLandHTML5+tag,”suchas<html>,<title>,<header>,<main>,and<footer>inaboveexample.TheFaceletscompilerisawareofthisstandardXMLnamespaceandwillpassthroughallelementsas“generalUIinstructions.”The“h”XMLnamespacespecifiestheJSFHTMLtaglibURIandthe“ui”XMLnamespacespecifiesthe

Page 339: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

JSFFaceletstaglibURI,whicharebothpresentintheJSFimplementationJARfileandregisteredtoFaceletsduringthewebapplication’sstartup.TheFaceletscompilercanthiswayfindtheassociatedtaghandlers,components,andcompositecomponents,whichinturndothehardworkofbuildingtheview,decodingtheHTTPrequest,andencodingtheHTTPresponse.ThoseURIsarethusnotperdefinitionliveInternetaddresses.Youcanevenspecifyyourownviaa*.taglib.xmlfile.Thiswillbeexpandedlaterinthesection“TagFiles.”

FollowingiswhattheincludefileWEB-INFincludes/layout/header.xhtmllookslike:<ui:composition

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

>

<ahref="#{request.contextPath}/">

<h:graphicImagename="images/layout/logo.svg"/>

</a>

<nav>

<ul>

<li><h:linkoutcome="about"value="About">

</li>

<li><h:linkoutcome="help"value="Help">

</li>

<li><h:linkoutcome="contact"

value="Contact"></li>

</ul>

</nav>

Page 340: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</ui:composition>

Notethelinkaroundthelogo.Itpointsto#{request.contextPath}/.Thisbasicallyprintsthedomain-relativeURLtotheapplication’sroot.#{request}isanimplicitELobjectreferringthecurrentHttpServletRequestinstance.contextPathreferstooneofitsproperties,impliedbythegetContextPath()method.

FollowingiswhattheincludefileWEB-INFincludes/layout/footer.xhtmllookslike:<ui:composition

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

>

<nav>

<ul>

<li><h:linkoutcome="/terms-of-service"

value="TermsofService"><li>

<li><h:linkoutcome="/privacy-policy"

value="PrivacyPolicy"><li>

<li><h:linkoutcome="/cookie-policy"

value="CookiePolicy"><li>

</ul>

</nav>

<small>©ExampleCompany</small>

</ui:composition>

Finally,here’showthetemplateclientcanlook,e.g.,/home.xhtml:<ui:compositiontemplate="WEB-

Page 341: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

INFtemplates/layout.xhtml"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

>

<ui:paramname="title"value="Welcome!"/>

<ui:definename="content">

<h1>WelcometoExampleCompany!</h1>

<p>Loremipsumdolorsitamet.</p>

</ui:define>

</ui:composition>

Notethetemplateattributeofthe<ui:composition>.Thismustrepresenttheserver-sidepathtothemastertemplate,preferablyasanabsolutepath,thusstartingwith“/”.

Inatemplateclient,<ui:param>letsyoudefineasimpleparameterspecificforthemastertemplate.Basically,youcanuseanyparametername,aslongasitissupportedbythemastertemplateanddoesn’tclashwithanexistingmanagedbeanname.Inthisspecificcase,thevaluefortheELvariable#{title}isspecifiedas“Welcome!”.Thiswillultimatelyendupinsidethe<title>elementofthemastertemplate.

Andthe<ui:define>letsyoudefineablockofmarkupspecificforthemastertemplate.Itwillultimatelyendupintheplacewherethe<ui:insert>withexactlythesamenameisdeclaredinthemastertemplate.Figure7-1givesaclearoverviewofhowthisallfitstogether.

Page 342: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure7-1 Therelationshipbetweenthemastertemplatelayout.xhtml,includefilesheader.xhtml,andfooter.xhtml,andthetemplateclienthome.xhtml.Notethattemplatefilepathsandsometagattributesareomittedforbrevity.Referthepreviouslyshowncodesnippetsfortheactualcoding.

Finally,opening/home.xhtmlshouldproducethefinalHTMLoutputwhichyoucaninspectbyright-clickingViewpagesourceintheaveragewebbrowser.

SinglePageApplicationArecenttrendistheso-calledSinglePageApplication(SPA).Thisconceptisonitsownnotsonew;infact,itisolderthanJSFitself,butitwasheavilypopularizedduring“web2.0”withJavaScript-basedframeworkssuchasAngular.Basically,anSPAletsthewebapplicationbehavelikeadesktop-orientedapplicationbydynamicallychangingthemaincontentwithanAjaxrequestwhennavigatingtoadifferentpageinsteadofloadingtheentirepageviaaGETrequest.GmailisonesuchknownexampleofanSPA.

SuchanSPAisalsoachievablewithJSFbysimplyusing

Page 343: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<ui:include>whosesrcattributeisdynamicallyupdatedbyAjax.Followingisanexampleutilizingthesamemastertemplateasshownintheprevioussection,/spa.xhtml:<ui:compositiontemplate="WEB-INFtemplates/layout.xhtml"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:jsf="http://xmlns.jcp.org/jsf"

xmlns:f="http://xmlns.jcp.org/jsf/core"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

>

<ui:paramname="title"value="SinglePage

Application"/>

<ui:definename="content">

<aside>

<nav>

<h:form>

<f:ajaxrender=":content">

<ul>

<li><h:commandLink

value="page1"

action="#

{spa.set('page1')}"><li>

<li><h:commandLink

value="page2"

action="#

{spa.set('page2')}"><li>

<li><h:commandLink

value="page3"

action="#

Page 344: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

{spa.set('page3')}"><li>

</ul>

</f:ajax>

</h:form>

</nav>

</aside>

<articlejsf:id="content"data-page="#

{spa.page}">

<ui:includesrc="WEB-INFincludes/spa/#

{spa.page}.xhtml"/>

</article>

</ui:define>

</ui:composition>

Donotethatthe<article>elementisdeclaredasaso-calledpassthroughelementbyexplicitlyspecifyingaJSFidentifierviajsf:id="…".ThisfeaturewasintroducedinJSF2.2.Underthehood,whendeclaringanHTMLelementwhichhasnoJSFcomponentequivalent,suchas<header>,<footer>,<main>,<article>,<section>,etc.asapassthroughelementthisway,itisturnedintoaUIPanelcomponentandistreatedintheJSFcomponenttreeexactlylike<h:panelGroup>.ThiswayyoucancleanlykeepusingsemanticHTML5markupwhilestillbeingabletoreferenceitasifitwereaJSFcomponentandthusbeabletoAjax-updateit.

Asyoumighthavedecipheredintheabove/spa.xhtmlexample,there’sasidenavigationmenuwhichsetsthecurrentpageinthemanagedbeanidentifiedby#{spa}andAjax-updatesthecomponentidentifiedbyid="content"whichinturncontainsadynamicinclude.Theaboveexample

Page 345: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

exceptsthefollowingincludefilestobepresentintheWEB-INFincludes/spafolder:page1.xhtml,page2.xhtml,andpage3.xhtml.Eachofthemisasimpleincludefilewhichlooksasfollows:<ui:compositionxmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

>

<h1>Firstpage</h1>

<p>Loremipsumdolorsitamet.</p>

</ui:composition>

Thebackingbeanassociatedwiththe#{spa}managedbeanisfairlysimple;itlooksasfollows:@Named@ViewScopedpublicclassSpaimplementsSerializable{

privateStringpage;

@PostConstruct

publicvoidinit(){

page="page1";

}

publicvoidset(Stringpage){

this.page=page;

}

publicStringgetPage(){

returnpage;

}

}

Page 346: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

[email protected],theusermightfaceanerrorpagewiththemessage“Invalidpath:WEB-INFincludes/spa/.xhtml”.

Notethatthebackingbeanisdeclared@ViewScoped.Thisisimportantinordertorememberacrosspostbackswhichpageiscurrentlybeingopened.Ifitwere@RequestScoped,andtheusernavigatesto,e.g.,page2andsubmitsaformtherein,whichcreatesanewHTTPrequest,thenthe@RequestScopedmanagedbeanwouldberecreatedagain,withpage1aspagevalueandthusnotpage2.Thishastheconsequencethat<ui:include>won’treferencepage2.xhtmlwhenJSFisabouttodecodeanyinputcomponentsinordertoprocesstheformsubmitduringthepostbackrequest,andthereforeJSFwouldfailtofindtheinputcomponentsdeclaredinpage2.xhtml.A@ViewScopedbeanlivesaslongastheuserpostbackstotheverysameview,inthiscase/spa.xhtml,andthereforecorrectlyremembersthecurrentlyselectedpage.

WhenplayingaroundwiththisSPAexample,youmighthavenoticedonedisadvantage:thepagesarenotbookmarkable.ThisiscausedbythefactthatthepagesarenotopenedbyanidempotentGETrequest.YoucansolvethatbyutilizingtheHTML5history.pushStateAPI.Basically,oncompletionoftheAjaxrequestyoushouldpushtheintendedURLtothebrowserhistory,whichwillbereflectedinthebrowser’saddressbar.And,youshouldmodifytheSpabackingbeantocheckifanyspecificpagehasbeenopenedandthenpreparethepagevariableaccordingly.

Followingisakickoffexamplewhichjustappendsthe?

4

Page 347: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

page=xxxquerystringparameter.Firstadjustthe<f:ajax>ofthespa.xhtmltospecifytheoneventattributeasfollows:<f:ajax...onevent="pageChangeListener">

AndcreatethefollowingJavaScriptfunction:functionpageChangeListener(event){

if(event.status=="success"){

varpage=

document.getElementById("content").dataset.page;

varurl=location.pathname+"?page="+page;

history.pushState(null,document.title,url);

}

}

And,finally,adjusttheSpabackingbeanasfollows:@Inject@ManagedProperty("#{param.page}")

PrivateStringpage;

publicvoidinit(){

if(page==null){

page="page1";

}

}

Note

The@ManagedPropertyiscurrentlyavailableintwoflavors:thedeprecatedonefromthejavax.faces.beanpackageandtheJSF2.3introducedonefromthejavax.faces.annotationpackage.

Page 348: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Youneedthelatterone.Also,notethatyoumightwanttovalidatetheprovidedpageparameter.Pathprobingbyhackersisinnocent,bytheway,asJSFalreadydoesn’tallowtraversingintotheparentpathasin/spa.xhtml?page=../../templates/layout.

TemplateDecorationsIncaseyouwouldliketohaveareusableincludefilewhichiscapableofinsertingtemplatedefinitionsasifyouwouldbeusing<ui:include>toreferenceanincludefilewithoneormore<ui:insert>sections,thenyoucanuse<ui:decorate>.Followingisonesuchexample,theWEB-INFdecorations/contact.xhtml:<ui:compositionxmlns:="http://www.w3.org/1999/xhtml"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

>

<sectionclass="contact">

<header><ui:insert><header>

<nav>

<ul>

<li>✆<ahref="tel:+31612345678"

title="Phone">+31(0)612345678</a>

</li>

<li>✉<ahref="mailto:[email protected]"

title="Email">[email protected]</a>

</li>

</ul>

</nav>

</section>

</ui:composition>

Page 349: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Hereishowitcanbeused,youcanputthe<ui:decorate>anywhereinyourtemplateclient,asa<ui:include>:<ui:decoratetemplate="WEB-

INFdecorations/contact.xhtml">

<h2>Questions?Contactus!</h2>

</ui:decorate>

Notethatthecontact.xhtmlhasonlyone<ui:insert>andthatithasnoname.Thiswillinserttheentire<ui:decorate>tagbodyatthedeclaredplaceof<ui:insert>.Youcan,ofcourse,specifyaname,butthenyouwouldneedtoexplicitlyspecify<ui:define>withanameforthat.Thatwouldbeonlyusefulifyouhavemorethanoneinsertsections.

Youcan,ifnecessary,use<ui:param>topassparameters.Thisworksthesamewayaswith<ui:compositiontemplate>.Thefollowingexampleparameterizesthee-mailusernameWEB-INFdecorations/contact.xhtmlwithadefaultvalueof“info.”

...

<li>✉<ahref="mailto:#{emptymailto?'info':

mailto}@example.com"

title="Email">#{emptymailto?'info':

mailto}@example.com</a></li>

...

Thiscanthenbeusedasfollows:

<ui:decoratetemplate="WEB-INFdecorations/contact.xhtml">

<ui:paramname="mailto"value="press"/>

<h3>Contactus</h3>

Page 350: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<p>

Forpressinquiriesyoucancontactusbythebelow

phonenumberandemailaddress.

</p>

</ui:decorate>

TagFilesAswith<ui:compositiontemplate>and<ui:decorate>,youcanalsouse<ui:param>in<ui:include>.However,watchoutthatyoudon’tgooverboard.

<ui:includesrc="WEB-INFincludes/field.xhtml">

<ui:paramname="id"value="firstName"/>

<ui:paramname="label"value="FirstName"/>

<ui:paramname="value"value="#{profile.user.firstName}"

/>

</ui:include>

WhereintheWEB-INFincludes/field.xhtmllookssomethinglikethefollowing:<ui:compositionxmlns:="http://www.w3.org/1999/xhtml"

xmlns:jsf="http://xmlns.jcp.org/jsf"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

xmlns:a="http://xmlns.jcp.org/jsf/passthrough"

xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"

>

<divclass="field"jsf:rendered="#{renderedne

false}">

<h:outputLabelid="#{id}_l"for="#{id}"value="#

{label}"/>

Page 351: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<c:choose>

<c:whentest="#{typeeq'password'}">

<h:inputSecretid="#{id}"label="#

{label}"

value="#{value}">

</h:inputSecret>

</c:when>

<c:whentest="#{typeeq'textarea'}">

<h:inputTextareaid="#{id}"label="#

{label}"

value="#{value}">

</h:inputTextarea>

</c:when>

<!--Moretypescanbeaddedasc:whenhere

-->

<c:otherwise>

<h:inputTextid="#{id}"label="#{label}"

value="#{value}"a:type="#{type}">

</h:inputText>

</c:otherwise>

</c:choose>

<h:messagesid="#{id}_m"for="#{id}"

styleClass="messages"/>

</div>

</ui:composition>

Insuchacase,youwouldprefertohavesomethingmoreconcise,likethefollowing,instead:<t:fieldid="firstName"label="FirstName"

value="#{profile.user.firstName}">

</t:field>

Page 352: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Having<ui:include>withtwoormore<ui:param>isastrongsignthattheincludefilecanbetterberegisteredasatagfilesothatitcanbeusedwithlessboilerplatecodeintheFacelet.

Firstmovetheincludefileintoadifferentsubfolder,WEB-INFtags/field.xhtml.Thisisnotatechnicalrequirement.Itwillworkjustfinewhereveryouputit,butwejustwanttoorganizethefilesclearly.MastertemplatefilesgoinWEB-INFtemplates,includefilesgothereinWEB-INFincludes,decoratefilesgoinWEB-INFdecorations,andtagfilesgoinWEB-INFtags.

Then,createthefollowingWEB-INFexample.taglib.xml:<?xmlversion="1.0"encoding="UTF-8"?>

<facelettaglib

xmlns:="http://xmlns.jcp.org/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-

instance"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/java

ee

http://xmlns.jcp.org/xml/ns/javaee/web-

facelettaglibrary_2_3.xsd"

version="2.3"

>

<namespace>http://example.com/tags</namespace>

<short-name>t</short-name>

<tag>

<description>Renderslabel+input+message

field.</description>

Page 353: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<tag-name>field</tag-name>

<source>tags/field.xhtml</source>

<attribute>

<description>Thetypeoftheinput

component.</description>

<name>type</name>

<required>false</required>

<type>java.lang.String</type>

</attribute>

<attribute>

<description>TheIDoftheinputcomponent.

</description>

<name>id</name>

<required>true</required>

<type>java.lang.String</type>

</attribute>

<attribute>

<description>Thelabeloftheinput

component.</description>

<name>label</name>

<required>true</required>

<type>java.lang.String</type>

</attribute>

<attribute>

<description>Thevalueoftheinput

component.</description>

<name>value</name>

<required>false</required>

<type>java.lang.Object</type>

</attribute>

<attribute>

Page 354: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<description>Whetherthefieldisrendered.

</description>

<name>rendered</name>

<required>false</required>

<type>boolean</type>

</attribute>

</tag>

</facelettaglib>

That’sadmittedlyquitesomeboilerplatecode.Itisgoodtoknowthatthe<attribute>elementsaren’tmandatoryforthetechnicalfunctioningofthetagfile.Youcouldevenomitthemaltogether.ButthentheIDEwon’tbeabletoloadthemintoautosuggestboxeswhileattemptingtoautocompletethecustomtag.Thisisnotreallydeveloper-friendly.Soyou’dbetterkeepthemin.The<required>propertyofthetagattribute,bytheway,onlyresultsinaruntimeerrorwhentheJSFprojectstageissettoDevelopment.InotherJSFprojectstages,it’signored.AndtheaverageIDEwillimmediatelypromptthoserequiredattributesduringautocompletingthetag.

Thefilename,example.taglib.xml,isfreetoyourchoice.InorderforJSFtoautomaticallypickupataglibfileduringtheapplication’sstartup,thereareonlytworequirements:itmusthavea.taglib.xmlextensionanditmustbeplacedin/WEB-INFfolder(orincaseofaJARfilewhichendsupinWEB-INFlib,thenin/META-INFfolderofthatJARfile).Unfortunately,placingthefilein/WEB-INFdoesn’talwaysworkquitewellinsomeservers,suchasGlassFish/Payara.Inthatcase,you’dhavetoexplicitlyregisteritviathefollowingcontextparameterinweb.xml

Page 355: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

whosevaluerepresentsthefullpathtothe*.taglib.xmlfilefromthewebrooton.

<context-param>

<param-name>javax.faces.FACELETS_LIBRARIES</param-name>

<param-value>WEB-INFexample.taglib.xml</param-value>

</context-param>

Inordertouseanytagdefinedinthe*.taglib.xml,youfirsthavetodeclareexactlythe<namespace>URIofthetaglibinyourFacelet,alongwithanarbitraryXMLnamespaceprefix.Forbettermaintainabilityofthecodeit’srecommendedtopickthetaglib’spreferredXMLnamespaceprefixasspecifiedinits<short-name>,whichis“t”incaseofourexample.taglib.xml.

<ui:compositiontemplate="WEB-INFtemplates/layout.xhtml"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

xmlns:t="http://example.com/tags"

>

<ui:paramname="title"value="LogIn"/>

<ui:definename="content">

<h:form>

<fieldset>

<header>

<h1>LogIn</h1>

</header>

<t:fieldtype="email"id="email"

label="Email"

value="#{login.email}">

</t:field>

<t:fieldtype="password"id="password"

label="Password"

Page 356: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

value="#{login.password}">

</t:field>

<footer>

<t:buttonid="submit"label="LogIn"

action="#{login.submit()}">

</t:button>

</footer>

</fieldset>

</h:form>

</ui:define>

</ui:composition>

Donotethatthetype="email"ofthee-mailfieldthusendsupin<c:otherwise>ofthetagfileimplementationwhereinitgetspassedthroughthea:type="#{type}"attributeof<h:inputText>.ThisallowsyoutoeasilyuseHTML5inputfields,suchastype="email",type="number",type="tel",etc.Thetypeattributebeingdefinedasapassthroughattributea:typeismandatory,becausethe<h:inputText>bydefaultignoresanycustomtypeattributeandstubbornlyrenderstype="text".

Youmightalsohavenoticedanothercustomtag,<t:button>.Here’showitisimplementedinWEB-INFtags/button.xhtml.

<ui:composition

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:jsf="http://xmlns.jcp.org/jsf"

xmlns:f="http://xmlns.jcp.org/jsf/core"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

>

<divclass="button"jsf:rendered="#{renderednefalse}">

<h:commandButtonid="#{id}"value="#{label}">

Page 357: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:actionListenerbinding="#{action}"/>

<f:ajaxexecute="@form"render="@form"/>

</h:commandButton>

<h:messagesid="#{id}_m"globalOnly="true"

redisplay="false"/>

</div>

</ui:composition>

It’sregisteredinexample.taglib.xmlnearlythesamewayas<t:field>,withoneexceptionfortheactionattribute.Technically,youneedtospecifya<method-signature>insteadofa(property)<type>:<attribute><description>

Actionmethodofthebutton.

NOTE:mustincludemethodparenthesis.

</description>

<name>action</name>

<required>true</required>

<method-signature>voidaction()</method-signature>

</attribute>

Youmighthavenoticedthattheactualtagimplementationuses<f:actionListenerbinding="#{action}">insteadofaction="#{action}".Thisisactuallyanecessarytrickinordertogetittoproperlyinvokethemethod.Thatis,the<method-signature>wasinitiallyintendedforUIcomponents,notfortagfiles.It’signoredintagfiles.ThismaybeworkedonforJSF.next.Fornow,youcangetawaywiththe<f:actionListenerbinding>trick.Thishasonlyoneadditionalrequirement:youneedtoexplicitlyincludethemethodparenthesisinthetagfileclientasin<t:buttonaction="#{login.submit()}">.

Page 358: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<t:buttonaction="{login.submit}">willotherwisefailwithjavax.el.PropertyNotFoundException:Theclass‘com.example.project.view.Login’doesnothavetheproperty‘submit’.

Incaseyouwouldliketocustomizetagfilesfromthetagfileclientsideon,e.g.,byaddingmorespecificinputattributes,ornestingcoretags,orbyprependingorappendingcontenttothelabelormessage,thenyoucanuse<ui:define>and<ui:insert>thesamewayyou’reusedtodoingwithmastertemplatefilesanddecoratefiles.ThefollowingexampledemonstrateshowyoucanenhancetheWEB-INFtags/field.xhtmlonthiswithabunchofnew<ui:insert>tags:<ui:compositionxmlns:="http://www.w3.org/1999/xhtml"

xmlns:jsf="http://xmlns.jcp.org/jsf"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

xmlns:a="http://xmlns.jcp.org/jsf/passthrough"

xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"

>

<divclass="field"jsf:rendered="#{renderedne

false}">

<ui:insertname="beforeLabel"/>

<ui:insertname="label">

<h:outputLabelid="#{id}_l"for="#{id}"

value="#{label}">

<span><ui:insertname="insideLabel">

<span>

</h:outputLabel>

</ui:insert>

Page 359: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<ui:insertname="beforeInput"/>

<ui:insertname="input">

<c:choose>

<c:whentest="#{typeeq'password'}">

<h:inputSecretid="#{id}"label="#

{label}"

value="#{value}">

<ui:insert/>

</h:inputSecret>

</c:when>

<c:whentest="#{typeeq'textarea'}">

<h:inputTextareaid="#{id}"label="#

{label}"

value="#{value}">

<ui:insert/>

</h:inputTextarea>

</c:when>

<!--Moretypescanbeaddedasc:when

here-->

<c:otherwise>

<h:inputTextid="#{id}"label="#

{label}"

value="#{value}"a:type="#

{type}">

<ui:insert/>

</h:inputText>

</c:otherwise>

</c:choose>

</ui:insert>

<ui:insertname="beforeMessages"/>

<ui:insertname="messages">

Page 360: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:messagesid="#{id}_m"for="#{id}"

styleClass="messages"/>

</ui:insert>

</div>

</ui:composition>

Now,that’salotofflexibility!Inthetagfileclientyoucanuse<ui:definename="beforeLabel">todefinesomecontentwhichshouldappearbeforethelabelofthefield.Andyoucanuse<ui:definename="label">tooverridethelabelaltogether.Andyoucanuse<ui:definename="insideLabel">toappendsome(HTML)contentinsidethelabel,andsoforth.ThefollowingexampledemonstrateshowinsideLabelcanbeusedtoappenda“Forgotpassword?”linktothelabelofthepasswordfield:<t:field...>

<ui:definename="insideLabel">

<h:linkoutcome="reset-password"value="Forgot

password?">

</ui:define>

</t:field>

Notethat<ui:insertname="insideLabel">iswrappedinanHTML<span>.Thisallowsyoutomoreeasilyselect“anything”thatendsupinthereviaCSS,sothatyoucan,forexample,letitfloattotherightusingjust.fieldlabel>span{float:right;}.

Anythingelseinthetagfileclientwhichisnot<ui:define>willendupinsidethenameless<ui:insert>tagnestedinthechoseninputcomponent.Thisallowsyoutoeasilynestany<f:xxx>coretag

Page 361: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

specificallyfortheinputcomponent:<t:field...><f:attributename="onkeypress"value="return

event.key!='Enter'"/>

<f:validateRegexpattern="[0-9]{4}"/>

<f:ajaxrender="otherField"/>

</t:field>

ThisspecificexamplepreventstheformfromsubmittingwhentheEnterkeyispressedbyreturningfalsewhentheKeyBoardEvent.keyequals“Enter”,andregistersaregexvalidatortoacceptonlyavalueoffourdigitsbymatchingagainstaregularexpressionpatternof“[0-9]{4}”,andinstructsJSFtoupdatethecomponentidentifiedby“otherField”byAjaxwhenthevaluechangeeventhasoccurred.

ComingbacktothetagfileimplementationinWEB-INFtags/field.xhtml,youmighthavenoticedthatgoodoldJSTLisbeingusedthereinsteadofJSF’sownrenderedattribute.ThishasadvantagesasJSTLhasadifferentlifecyclethanJSFUIcomponents.JSTLisexecutedwhentheJSFcomponenttreeisabouttobebuilt,duringtheviewbuildtime.JSFcomponentsareexecutedwhenHTMLoutputisabouttobegenerated,duringtheviewrendertime.Moreover,ifyouwereusingJSF’sownrenderedattribute,thenyouwouldface“duplicatecomponentID”errorsbecauseofmultiplecomponentswiththesameIDphysicallyendingupintheJSFcomponenttree.

MightithappenthatyouareconsideringtheuseofplainJavacodetodynamicallycreatethecomponenttreebasedonatleastaview-scopedmodel,youshouldabsolutelyreconsiderusingJSTLinstead.AsJSTLitselfisalsoXML-basedandyou

Page 362: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

canthusjustputtogethereverythinginanXHTMLfile,youwillendupwithmuchbetterreadableandmaintainablecodefora“dynamic”component.

CompositeComponentsSometimes,youwouldliketohaveagroupofrelatedinputcomponentstorepresentasinglemodelvalue.Aclassicexampleishavingthree<h:selectOneMenu>drop-downsrepresentingday,month,andyearwhichareultimatelyboundtoasinglejava.time.LocalDatepropertyinthebackingbean.Thisisnottrivialtoimplementwithjustanincludefileoratagfile.YouwouldneedsomeadditionalJava-basedlogicwhichmakessurethat,e.g.,thedaydrop-downdoesn’tshowthevalues29,30,or31dependingonthecurrentlyselectedmonth,andthatitconvertsthesubmittedvaluestoafull-fledgedLocalDateinstance,andviceversa.Butyoucan’tandshouldn’tputanyJavacodeinanyFacelet.

You’llperhapsthinkofjustcreatingadedicatedbackingbeanforthiscase.Butthisisnotsufficienteither.Itdoesn’tallowyoutocleanlyhookonthecomponent’slifecyclethroughtheJSFphases:collectingtheindividualsubmittedvaluesfrommultiplecomponents,convertingthemintoasingleLocalDateinstance,ifnecessarythrowingaconverterexceptionduringthevalidationsphase,andlettingtheJSFlifecycleautomaticallyskiptheremainingphases.Abackingbean’ssettermethodoractionmethodisfarfromtherightplaceforthatlogic.Itwouldbeinvokedtoolateanyway.And,itwouldfeelstrangetobeabletoreferenceandpotentiallymanipulatetheverysamebackingbeanviaanELexpressiononanarbitraryplaceintheFacelet.

Page 363: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Thisisexactlywherecompositecomponentscomeintothepicture:composingabunchofexistingJSFcomponentsintovirtuallyasinglecomponenttiedtoasinglemodelvalueandultimatelyusingitexactlythesamewayasyouwouldbeusingaplain<h:inputText>.Imaginea<t:inputLocalTime>compositecomponentcomposedoftwo<h:selectOneMenu>componentstiedtoasinglejava.time.LocalTimemodelvalue.Insteadofabackingbean,youcanuseafull-fledgedUIComponentinstanceasaso-calledbackingcomponent.

Firstcreateadedicatedsubfolderinmain/webapp/resourcesfolder(andthusnotmain/java/resources!),forexample,main/webapp/resources/components.ThereyoucanputFaceletsfilesrepresentingcompositecomponents.ThesubfolderisthentobeusedintheXMLnamespaceURIafterhttp://xmlns.jcp.org/jsf/compositeasshownnext.

xmlns:t="http://xmlns.jcp.org/jsf/composite/components"

NotethattheXMLnamespaceprefixof“t”clasheswithonewhichwealreadydefinedbeforefortagfiles.Thisisofcoursenottheintent.YoumaychooseadifferentXMLnamespaceforcompositecomponents.It’s,however,alsopossibletoletthemsharethesamecustomXMLnamespaceURIhttp://example.com.tags.Thiscanbeachievedbyaddinga<composite-library-name>tothe*.taglib.xmlwhichinturnmustrepresentthenameofthededicatedsubfolder.

Page 364: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<composite-library-name>components</composite-library-name>

ThiswayallcompositecomponentsarealsoavailablebythesameXMLnamespaceastagfiles.

<...xmlns:t="http://example.com/tags">

...

<t:inputLocalTime.../>

ThefilenameoftheFaceletsfilerepresentingthecompositecomponentwillbecomethetagname.So,inordertohavea<t:inputLocalTime>,weneedaninputLocalTime.xhtmlfileinthemain/webapp/resources/componentsfolder.Followingisakickoffexampleofwhatitcanlooklike:<ui:component

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:f="http://xmlns.jcp.org/jsf/core"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

xmlns:cc="http://xmlns.jcp.org/jsf/composite"

>

<cc:interfacecomponentType="inputLocalTime">

<cc:attributename="value"

type="java.time.LocalTime"

shortDescription="Selectedtime.Defaultsto

00:00.">

</cc:attribute>

<cc:attributename="required"type="boolean"

shortDescription="Requiredstate.Defaults

tofalse.">

Page 365: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</cc:attribute>

</cc:interface>

<cc:implementation>

<spanid="#{cc.clientId}"

class="inputLocalTime">

<h:selectOneMenuid="hour"binding="#

{cc.hour}"

required="#{cc.attrs.required}">

<f:selectItemitemValue="#{null}"/>

<f:selectItemsvalue="#{cc.hours}"/>

</h:selectOneMenu>

:

<h:selectOneMenuid="minute"binding="#

{cc.minute}"

required="#{cc.attrs.required}">

<f:selectItemitemValue="#{null}"/>

<f:selectItemsvalue="#{cc.minutes}"/>

</h:selectOneMenu>

</span>

</cc:implementation>

</ui:component>

Thereareseveralthingstonoticeherewhichmakesacompositecomponentdifferentfromatagfile.First,thecompositecomponent’sbodyisalwaysdividedintotwosections:aninterfaceandanimplementation.

TheinterfacedeclaresacomponentTypeattributewhichshouldreferencethevalueofeitherthe@FacesComponentannotationonaUIComponentsubclassorthe<component-type>entryofa<component>asdeclaredineitherfaces-config.xml

Page 366: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

or*taglib.xml.WhenthecomponentTypeattributeisabsent,itdefaultstoUINamingContainer.Theinterfacealsodeclaresthesupportedattributes.Tokeepitsimple,werestricttoonlytwo:valueandrequired.TherearealsoimplicitlyinheritedattributesfromtheUIComponentsuperclass,whichwedon’tneedtoexplicitlydefineas<cc:attribute>:id,binding,andrendered.Thatmakesitatotaloffiveattributeswhichyoucanuseintheimplementation.

Theimplementationdefinestheactualmarkupofthecompositecomponent.Thereyoucanfindthetwo<h:selectOneMenu>drop-downswrappedina<span>element.ThereyoucanalsofindseveraloccurrencesofthespecialELvariable#{cc}whichreferstothecurrentUIComponentinstancebehindthecompositecomponent,whichisthusoneofthetypeasdeclaredinthecomponentTypeattribute,or,ifabsent,aUINamingContainer.#{cc.attrs}isashortcuttothecomponentattributemapasavailablebyUIComponent#getAttributes().#

{cc.attrs.required}asusedin<t:inputLocalTime>thusrefersto<cc:attributename="required">.

#{cc.clientId}inthe<span>elementjustprintsthecompositecomponent’sclientIDastheidattributeof<span>.Thisisactuallyatrickinordertobeabletoreferencethe“whole”compositecomponentusingaclientIDsearchexpressionfromthetemplateclienton.Imaginethefollowingcase:<h:inputText...><f:ajaxrender="time"/>

Page 367: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</h:inputText>

<t:inputLocalTimeid="time".../>

Thiscasewouldn’thaveworkedwithoutthe#{cc.clientId}beingrenderedasanIDofanyplainHTMLelementwhichwrapstheentirebodyof<cc:implementation>,usuallya<span>or<div>.Thetechnicalproblemis,whilethecompositecomponentitselfisfindableintheJSFcomponenttreebythecomponentIDsearchexpression,theHTMLrepresentationofthecompositecomponentisbydefaultnotavailablebydocument.getElementById(clientId)inJavaScript.Inotherwords,JSFAjaxwouldn’tbeabletoupdateit.ExplicitlyaddingaplainHTMLelementwiththecompositecomponent’sclientIDthussolvesthat.

Finally,thereareabunchof#{cc}expressionswhichdon’treferencetheattributesdirectly.Bothofthe<h:selectOneMenu>drop-downsaredirectlyboundaspropertiesoftheso-calledbackingcomponent,theconcreteUIComponentinstancebehindthecompositecomponent.And,both<f:selectItems>optionsobtaintheirvaluesdirectlyfromthebackingcomponentaswell.Here’sthebackingcomponentclass,com.example.project.composite.InputLocalTi

me.

@FacesComponent("inputLocalTime")

publicclassInputLocalTimeextendsUIInputimplements

NamingContainer{

privatestaticfinalList<String>HOURS=

IntStream.rangeClosed(0,23).boxed()

Page 368: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

.map(InputLocalTime::pad).collect(Collectors.toLi

st());

privatestaticfinalList<String>MINUTES=

IntStream.rangeClosed(0,59).boxed()

.map(InputLocalTime::pad).collect(Collectors.toLi

st());

privateUIInputhour;

privateUIInputminute;

@Override

publicStringgetFamily(){

returnUINamingContainer.COMPONENT_FAMILY;

}

@Override

publicvoidencodeBegin(FacesContextcontext)throws

IOException{

LocalTimelocalTime=(LocalTime)getValue();

if(localTime!=null){

hour.setValue(pad(localTime.getHour()));

minute.setValue(pad(localTime.getMinute()));

}

super.encodeBegin(context);

}

@Override

publicObjectgetSubmittedValue(){

StringsubmittedHour=(String)

hour.getSubmittedValue();

StringsubmittedMinute=(String)

minute.getSubmittedValue();

if(submittedHour==null||submittedMinute==null)

{

returnnull;

}

elseif(submittedHour.isEmpty()||

Page 369: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

submittedMinute.isEmpty()){

return"";

}

else{

returnsubmittedHour+":"+submittedMinute;

}

}

@Override

protectedObjectgetConvertedValue

(FacesContextcontext,ObjectsubmittedValue)

{

StringsubmittedTime=(String)submittedValue;

if(submittedTime==null||submittedTime.isEmpty())

{

returnnull;

}

try{

returnLocalTime.parse(submittedTime,

DateTimeFormatter.ISO_LOCAL_TIME);

}

catch(DateTimeParseExceptione){

thrownewConverterException(e);

}

}

privatestaticStringpad(Integervalue){

returnString.format("%02d",value);

}

publicUIInputgetHour(){returnhour;}

publicvoidsetHour(UIInputhour){this.hour=hour;}

publicUIInputgetMinute(){returnminute;}

publicvoidsetMinute(UIInputminute){this.minute=

minute;}

publicList<String>getHours(){returnHOURS;}

publicList<String>getMinutes(){returnMINUTES;}

}

Page 370: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Now,thatwasabitofcode.Notonlywillyouseethatthegettersandsettersarecollapsedforbrevity,butyou’llalsoseethatourcompositeextendsUIInputandimplementsNamingContainer.ExtendingfromUIInputgivesusthebenefitthatwedon’tneedtorepeatmostofthedefaultencodinganddecodingbehaviorofUIInputinourbackingcomponent,soweonlyneedtooverrideafewmethods.ImplementingNamingContainerisatechnicalrequirementof<cc:interface>.Thisenablesyoutousemultipleinstancesofthecompositecomponentinthesamecontextwithoutfacing“duplicatecomponentID”errors.ThisrequirementisalsoreflectedbytheoverriddengetFamily()method,whichmustasperthecompositecomponent’scontractreturntheUINamingContainer.COMPONENT_FAMILYconstant.

TheactualUIInputcomponentscomposingthecompositecomponentaredeclaredaspropertiesofthebackingcomponent.Inthiscasetheyareboth<h:selectOneMenu>drop-downswhichare,viathebindingattribute,tiedtothoseproperties.Thisenablesustoeasilysettheirvaluesduringencoding(read:processingtheHTTPresponse),andtoobtainthesubmittedvaluesduringdecoding(read:processingtheHTTPrequest).YoucanfindthelogicforthatintheoverriddenencodeBegin()andgetSubmittedValue()methods,respectively.

IntheencodeBegin()methodyouthushavetheopportunitytopreparethedisplayedvaluesbasedonthemodelvalue,ifany.ThegetValue()methodisinheritedfromtheUIInputsuperclassandistiedtothevalueattribute.Youcanbreakdownthemodelvalueandsetthe

Page 371: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

desiredvaluesintheindividualUIInputcomponentsofthecompositecomponent.Thepad()helpermethodjustpadsthedigitwithaleadingzerosothat,e.g.,the“1”getsdisplayedas“01”.Thishelpermethodisalsousedduringstaticinitializationofthelistsofavailablehoursandminutesspecificallyfor<f:selectItems>.

InthegetSubmittedValue()methodyoushouldcomposethesubmittedvaluesoftheindividualUIInputcomponentstogethertoasingleString.Inthespecificcaseof<t:inputLocalTime>,wecomposeaStringfollowingtheISOlocaltimepatternHH:mm.Inturn,theUIInputsuperclasspassesthisvaluethroughthegetConvertedValue()whereinwethushavetheopportunitytoconvertthecomposedStringtotheconcretemodelvalue,whichisinourcasetheLocalTime.UltimatelytheUIInputsuperclasswillmakesurethatthisgetssetinthebackingbeanduringtheupdatemodelvaluesphase.Nowyoucanuseitasfollows:<ui:compositiontemplate="WEB-INFtemplates/layout.xhtml"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:f="http://xmlns.jcp.org/jsf/core"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

xmlns:cc="http://xmlns.jcp.org/jsf/composite/compone

nts"

>

<ui:definename="content">

<h:form>

<h:outputLabelfor="time:hour"value="Time"

/>

Page 372: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<t:inputLocalTimeid="time"value="#

{bean.time}"/>

<h:commandButtonvalue="Submit"action="#

{bean.submit}">

<f:ajaxexecute="@form"/>

</h:commandButton>

</h:form>

</ui:define>

</ui:composition>

wherethebackingbeanrepresentedby#{bean}looksasfollows:@Named@RequestScopedpublicclassBean{

privateLocalTimetime;

publicvoidsubmit(){

System.out.println("Submittedlocaltime:"+

time);

}

publicLocalTimegetTime(){

returntime;

}

publicvoidsetTime(LocalTimetime){

this.time=time;

}

}

Mightithappenthatyouneedtonest<f:ajax>insidethe

Page 373: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

compositecomponentinordertorunsomeAjaxduringthechangeeventofanyoftheindividualdrop-downs,thenyoucanachievethatbyadding<cc:clientBehavior>targetingbothdrop-downsasfollows:<cc:interface...>...

<cc:clientBehaviorname="change"default="true"

targets="hourminute"event="change">

</cc:clientBehavior>

</cc:interface>

Thenameattributerepresentstheeventnamewhichyoushoulddeclareintemplateclientinordertotriggerit.

<t:inputLocalTimeid="time"...>

<f:ajaxevent="change"execute="time".../>

</t:inputLocalTime>

Thedefault="true"indicatesthatthisisthedefaultevent,whichmeansthatyoucouldjustomitit,asyoucoulddowithevent="change"forexistinginputtextanddrop-downcomponents,andwithevent="click"forexistingcheckboxandradiobuttoncomponents,andwithevent="action"forexistingcommandcomponents.

<t:inputLocalTimeid="time"...>

<f:ajaxexecute="time".../>

</t:inputLocalTime>

Thetargetsattributemustdefineaspace-separatedcollectionofIDsoftargetUIInputcomponentsresemblingthecompositecomponentonwhichyouwouldliketotriggertheAjaxevent,andtheeventattributemustdefinethe

Page 374: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

desiredeventnametobeactuallytriggeredonthetargetUIInputcomponents.Inotherwords,thisdoes,underthehood,effectivelythesameasifthefollowingisimplementedinthecompositecomponent:<h:selectOneMenuid="hour"...>

<f:ajaxevent="change".../>

</h:selectOneMenu>

<h:selectOneMenuid="minute"...>

<f:ajaxevent="change".../>

</h:selectOneMenu>

Inthisspecificexample,theeventattributecoincidentallyhasjustthesamevalueasthenameattribute.Thisisperhapsconfusingatfirst,butitallowsyoutoeasilydefineacustomeventname.Forexample,followingisthecodetousewhenyouwanttotriggeraneventsolelyonthechangeofthehourdrop-down:<cc:interface...>...

<cc:clientBehaviorname="hourChange"

targets="hour"event="change">

</cc:clientBehavior>

</cc:interface>

Withthis,thefollowingAjaxlistenerwillthusbefiredonlywhenthehourdrop-downischangedandnotwhentheminutedrop-downischanged.

<t:inputLocalTimeid="time"...>

<f:ajaxevent="hourChange"execute="time"

listener="#{bean.hourChanged}">

</f:ajax>

</t:inputLocalTime>

Page 375: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Notethatexecute="time"isconsistentlyexplicitlyspecifiedinthegivenexamples.Thisisbecausethedefaultofexecute="@this"stilldoesnot,inthecurrentJSF2.3version,workcorrectlywithinthecontextofthecompositecomponentimplementation.JSFcouldhavederivedthetargetcomponentsfromany<cc:clientBehaviordefault="true">,butitisn’tspecifiedassuchyet.

Allinall,itmustbesaidthatcompositecomponentshavebeenoverhypedaftertheywerefirstintroducedinJSF2.0.Peoplestartedusingthemto“composite”wholetemplates,includes,decorations,andevenmultipletags,withoutusinganybackingcomponent.Thatis,thezero-configurationnatureascomparedtotagfilesisveryattractive.Everythingisdeclaredinthecompositecomponentfileitselfvia<cc:interface>.AndtheycanbedirectlyusedinthetemplateclientjustbyfollowingaconventionwithouttheneedtoconfigureitinsomeXMLfile.

Thecaveatisthatcompositecomponentsare,duetotheirinternaldesign,relativelyexpensiveduringbuildingandrestoringtheviewascomparedtoplainincludefiles,decoratefiles,andtagfiles,especiallywhendeeplynested.Thebestpracticeisthereforetousethemonlyifyouactuallyneedabackingcomponentvia<cc:interfacecomponentType>.Foranyothercase,justuseaninclude,adecorate,oratagfileinstead.IntheprevioussectionyoumayalreadyhavelearnedthattagfilescanbequitepowerfulwithhelpofJSTL.

RECURSIVECOMPOSITECOMPONENT

Page 376: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Youcansafelynestcompositecomponentsineachother.However,whenyounesttheverysamecompositecomponentrecursivelyinitself,thenitwouldfailwithastackoverflowerrorwhenELattemptstoresolvetheconcretecompositecomponentinstancebehind#{cc}.

Imaginethatyou’vegotarecursivetreemodelwhichrepresentssomesortofdiscussionthread,suchase-mailmessagesandalltheirreplies,orblogcommentsandalltheirreplies,wherebyeachreplycaninturnhaveanothersetofreplies.ThiscanberepresentedasasingleJPAentityasfollows:@EntitypublicclassMessage{

@Id@GeneratedValue(strategy=IDENTITY)

privateLongid;

@Lob

@Column(nullable=false)

private@NotNullStringtext;

@ManyToOne

privateMessagereplyTo;

@OneToMany(mappedBy="replyTo")

privateList<Message>replies=

Collections.emptyList();

//Add/generateremaininggettersandsetters.

}

NotethatthereplyTopropertyrepresentstheparent

5

Page 377: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

messagewhichthecurrentmessageisareplyto,andthattherepliespropertyrepresentsallrepliestothecurrentmessage.ThetreestructurecanthenbequeriedasfollowsinaMessageService:publicList<Message>tree(){returnentityManager.createQuery(

"SELECTDISTINCTmFROMMessagem"

+"LEFTJOINFETCHm.repliesr"

+"ORDERBYm.idASC",Message.class)

.getResultList().stream()

.filter(m->m.getReplyTo()==null)

.collect(toList());

}

Notethatthefilteringoftheresultlistafterwardisatfirstglanceinefficient,butinrealityeverysinglemessageisretrievedonlyonceandsimplyreferencedintherepliesproperty.

Now,you’dintuitivelyimplementthe<t:message>compositecomponentassomethinglikethefollowing:<cc:interface>

<cc:attributename="value"

type="com.example.Message"/>

</cc:interface>

<cc:implementation>

#{cc.attrs.value.text}

<c:iftest="#{notemptycc.attrs.value.replies}">

<ul>

<c:forEachitems="#{cc.attrs.value.replies}"

var="reply">

<li>

<t:messagevalue="#{reply}"/>

Page 378: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</li>

</c:forEach>

</ul>

</c:if>

</cc:implementation>

whichisinturnusedasfollows:

<c:forEachitems="#{messages.tree}"var="message">

<t:messagevalue="#{message}"/>

</c:forEach>

You’llperhapsonlywonderwhy<c:forEach>isbeingusedinsteadof<ui:repeat>.Theexplanationisrelativelysimple:<ui:repeat>isignoredduringviewbuildtime.Inotherwords,<t:message>wouldincludeitselfinaninfiniteloop.Ifyouneedtorememberthewhyandhow,headbacktothesection“JSTLCoreTags”inChapter3.

Butevenwiththeaboveimplementationyouwouldstillrunintoaninfiniteloop.Youknowthat#{cc}referencesthecurrentinstanceofthecompositecomponent.Underthehood,when#{reply}ispassedtothenestedcomposite,theninrealityareferenceto#{cc.attrs.value.replies[index]}isbeingpassed.Thisis,onitsown,noproblem.Butwhenthenestedcompositeinturnevaluatesthe#{cc}partfromthisalias,thenitwouldreferenceitselfinsteadoftheparentcomposite.Hence,theinfiniteloop.

Theoretically,youcouldsolvethisbyreplacing#{cc}with#{cc.parent}whichreturnsUIComponent#getParent().

Page 379: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<c:forEachitems="#{cc.attrs.value.replies}"

varStatus="loop">

...

<t:messagevalue="#

{cc.parent.attrs.value.replies[loop.index]}"/>

...

</c:forEach>

However,itstilldoesn’twork.Underthehood,insidethenestedcomposite,whentheELevaluatorcomesto#{cc.parent}andattemptstoevaluate“attrs.value”onit,thentheparentcompositecomponentwouldreturnyetanotherELexpressioninformof#{cc.attrs.value}whichultimatelygetsevaluated.However,the#{cc}partstillgetsinterpretedas“currentcompositecomponent,”whichisinsidethenestedcompositecomponentandthusthenestedcompositeitself.

WecouldonlysolveittolettheparentcompositecomponentnotreturnyetanotherELexpressionbutinsteadthealready-evaluatedvalue.ThiscanbeachievedbyoverridingUIComponent#setValueExpression()inthebackingcomponentwhereyoucheckwhethertheValueExpressionrepresenting#{cc.attrs.value}isabouttobesetonthecomponentandthenimmediatelyevaluateitandstoretheresultasalocalvariableofthecompositecomponent.Thisshouldn’tcauseharmasit’ssupposedtobearead-onlyattribute.

@FacesComponent("messageComposite")

publicclassMessageCompositeextendsUINamingContainer{

privateMessagemessage;

Page 380: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Override

publicvoidsetValueExpression

(StringattributeName,ValueExpressionexpression)

{

if("value".equals(attributeName)){

ELContextelContext=

getFacesContext().getELContext();

message=(Message)

expression.getValue(elContext);

}

else{

super.setValueExpression(attributeName,

expression);

}

}

publicMessagegetMessage(){

returnmessage;

}

}

Withthisbackingcomponentinplace,andreplacing“attrs.value”by“message”,itfinallyworks.

<cc:interfacecomponentType="messageComposite">

<cc:attributename="value"type="com.example.Message"/>

</cc:interface>

<cc:implementation>

#{cc.message.text}

<c:iftest="#{notemptycc.message.replies}">

<ul>

<c:forEachitems="#{cc.message.replies}"

varStatus="loop">

<li>

<t:message

value="#

{cc.parent.message.replies[loop.index]}">

</t:message>

</li>

Page 381: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</c:forEach>

</ul>

</c:if>

</cc:implementation>

ImplicitELObjectsInFaceletsfilesabunchofimplicitELobjectsavailable.Theyaremainlyshortcutstoimportantartifacts,scopes,maps,andcomponentsavailableinthecurrentfacescontext.Table7-1providesanoverviewofthem.

Table7-1 ImplicitELObjectsAvailableinELContextofJSF

ImplicitELobject

Resolvesto

Returns

Since

#{facesContext}

FacesContext#getCurrentInstance()

FacesContext

2.0

#{externalContext}

FacesContext#getExternalContext()

ExternalContext

2.3

#{view}

FacesContext#getViewRoot()

UIViewRoot

2.0

#{component}

UIComponent#getCurrentComponent()

UIComponent

2.0

Page 382: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

#{cc}

UIComponent#getCurrentCompositeComponent()

UIComponent

2.0

#{request}

ExternalContext#getRequest()

HttpServletRequest

1.0

#{session}

ExternalContext#getSession()

HttpSession

1.0

#{application}

ExternalContext#getContext()

ServletContext

1.0

#{flash}

ExternalContext#getFlash()

Flash

2.0

#{requestScope}

ExternalContext#getRequestMap()

Map<String,Object>

1.0

#{viewScope}

UIViewRoot#getViewMap()

Map<String,Object>

2.0

#{flowScope}

FlowHandler#getCurrentFlowScope()

Map<Object,Object>

2.2

#{sessionScop

ExternalContext#getSessionMap()

Map<String,Object>

1.0

Page 383: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

{sessionScope}

ExternalContext#getSessionMap() Object>

1.0

#

{applicationScope}

ExternalContext#getApplicationMa

p()

Map<String,

Object>

1.0

#{initParam}

ExternalContext#getInitParameterMap()

Map<String,String>

1.0

#{param}

ExternalContext#getRequestParameterMap()

Map<String,String>

1.0

#{paramValues}

ExternalContext#getRequestParameterValuesMap()

Map<String,String[]>

1.0

#{header}

ExternalContext#getRequestHeaderMap()

Map<String,String>

1.0

#{headerValues}

ExternalContext#getRequestHeaderValuesMap()

Map<String,String[]>

1.0

#{cookie}

ExternalContext#getRequestCookieMap()

Map<String,Cookie>

1.0

#{resource}

ResourceHandler#createResource()

Resource

2.0

Page 384: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

#{resource}

ResourceHandler#createResource()

Resource

2.0

FortheJSFartifactsandcomponents,iftheclassinturnspecifiesagettermethodsomewhere,suchasHttpServletRequest#getContextPath(),thenyoucanofcourseaccessitinELtheusualwayasin#{request.contextPath}.

Forscopedmaps,anypropertywillbeinterpretedasthemapkey.Ifthepropertyhappenstocontainperiodcharacters,thenyoucanusethebracenotationasin#{map['key.with.periods']}inordertoaccessthemapvalue.Notethat#{flash}essentiallyextendsfromMap<String,Object>,soitcouldbetreatedassuch.Itshouldalsobesaidthat#{flowScope}indeeddeviatesfromotherscopedmapsbyacceptingObjectinsteadofStringasamapkey.Thisismostlikelyahistoricalmistake.ThecanonicalapproachtoaccessscopedmapsistouseaString-basedkey.

#{cookie}ismappedbythecookienameandthevalueactuallyreturnsajavax.servlet.http.CookiewhichinturnhasagetValue()property.So,inordertoaccesstheJSESSIONIDcookie,youbasicallyneed#{cookie.JSESSIONID.value}.Ofcourse,youcanalsojustuse#{session.id}instead.

#{resource}actuallyhasitsownELresolverwhichinterpretsanypropertyasaresourceidentifierin“library:name”formatandthenpassesittoResourceHandler#createResource()andfinallyreturnstheURLoftheresourcevia

Page 385: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Resource#getRequestPath().ThisisveryusefulinCSSresourcesinordertoreferenceaJSFimageresourceasaCSSbackgroundimage.ThefollowingexamplewillactuallyrendertheURLofsrc/main/webapp/resources/images/backgroun

d.svg.

body{

background-image:url("#

{resource['images/background.svg']}");

background-size:cover;

}

NotethatresolvingELexpressionsinCSSresourcesonlyworkswhentheCSSresourceitselfisincludedvia<h:outputStylesheet>insteadof<link>.AlsonotedshouldbethatJSFremembersbydefaultonlyontheveryfirstrequestoftheCSSresourcewhetheritcontainsELexpressionsornot.Ifitdidn’t,thenJSFwon’trecheckitonalaterrequest,evennotinthedevelopmentstage.SoifyounoticethatyourfirstELexpressioninanexistingCSSresourcedoesn’tseemtowork,you’dbetterrestartthewebapplication.ThisfeatureofELresolvinginCSSresourcesisactuallyprettyuseful.IfSCSS(SassyCSS)isasteptoofarforyou,thenyoucoulduseELtoparameterizesomerepeatedCSSproperties,suchascolors.

.color-gray{

color:#{applicationScope['gray']='#B8B8B8'};

}

...

Page 386: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

.someSelector{

border:1pxsolid#{gray};

}

.otherSelector{

color:#{gray};

}

...

No,thisfeatureofELresolvinginCSSresourcesisn’tavailableinJSresources.Forthat,you’dinsteadneedtoprintaJSobjectintheglobalscopeandletyourJSresourcesinterceptitifnecessary.Forexample,<h:outputScript>varconfig=#{configuration.script};</h:outputScript>

<h:outputScriptname="scripts/some.js"/>

wherebythe#{configuration.script}justreturnsaJSONobjectasstringfromyourmanagedbean.Or,youcouldletELprintitasadataattributeofanHTMLelement<htmllang="en"data-baseuri="#{request.contextPath}/">

...

</html>

whichisinturnaccessibleinJSasfollows:varbaseuri=document.documentElement.dataset.baseuri;

orifyou’reajQueryfan:

varbaseuri=$("html").data("baseuri");

Thatsaid,whencreatingmanagedbeansontheJavaside,orwhendeclaringcustomELvariablesontheFaceletsside,suchas<h:dataTablevar="foo">,<ui:repeatvar="foo">,or<c:setvar="foo">,youneedto

Page 387: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

makeabsolutelysurethatyoudon’texplicitlyorimplicitlychooseamanagedbeannameorELvariablenamewhichclasheswithoneofthepreviouslylistedimplicitELobjects,becauseimplicitELobjectshavehigherprecedenceinELresolvingthanuserdefinednames.So,forexample,thefollowingconstructwouldn’tworkasyoumightexpect:<ui:repeatvalue="#{bean.parameters}"var="param">

#{param}<br/>

</ui:repeat>

Itwouldprintliterally“{}”foreachiterationround,whichisbasicallythedefaultMap#toString()formatofanemptyMap.Whenyoureopenthesamepagewithaquerystringlike?foo=bar,thenitwouldprintliterally“{foo=bar}”foreachiterationround.You’dbetterrenamethevar="param"tosomethingelsethen.

Footnotes1www.onjava.com/pub/a/onjava/2004/06/09/jsf.html.

2https://stackoverflow.com/q/1296235/157882..

3http://arjan-tijms.omnifaces.org/2011/09/authoring-jsf-

pages-in-pure-java.html.

4https://developer.mozilla.org/en-

US/docs/Web/API/History_API#Adding_and_modifying_history

_entries..

5http://balusc.omnifaces.org/2016/02/recursive-tree-of-

composite-components.html..

Page 388: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1) (2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_8

8.BackingBeans

BaukeScholtz andArjanTijms

Willemstad,Curaçao Amsterdam,Noord-Holland,TheNetherlands

The“backingbean”isaJSF-specificconcept.ItrepresentsthesoleJavaBeanclasswhichisultimatelyusedasa“managedbean”responsibleforprovidingdata,actions,and/orUI(UserInterface)componentsinaJSFpage.

Model,View,orController?JSF(JavaServerFaces)isaMVC(model-view-controller)framework.It’sawidelyusedarchitecturaldesignpatternforsoftwareapplicationswhichhasitsrootsindesktopapplicationdevelopment.

InaJSFframework’spointofview,themodelisrepresentedbythebackingbean,theviewisrepresentedbythecomponenttree,whichinturnisusuallydefinedinaFaceletsfile,andthecontrollerisrepresentedbytheFacesServletwhichisalreadyprovidedbyJSF.FromaJavaEEapplicationserver’spointofview,however,themodelisrepresentedbytheservicelayerwhichinturnis

1 2

1

Page 389: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

usuallydefinedinEJB(EnterpriseJavaBeans)classesandJPA(JavaPersistenceAPI)entities,theviewisrepresentedbyallyourJSF-basedcode,andthecontrolleristheFacesServlet.InaJSFdeveloper’spointofview,themodelisrepresentedbytheservicelayer,theviewisrepresentedbytheFaceletsfile,andthecontrollerisrepresentedbythebackingbean.

Thebackingbeanclasscanthusbeeitherthemodel,view,orcontroller,dependingonyourpointofview,whiletheservicelayerisalwaysthemodel,andtheFaceletsfileisalwaystheview,andtheFacesServletisalwaysthecontroller.Notethatinthiscontext,the“JSFdeveloper”isyou,whodevelopsawebapplicationusingtheJSFframeworkforaJavaEEapplicationserver.

Figure8-1illustratesthepositionofthebackingbeaninJSF’sMVCparadigm.It’saVenndiagramwheretheintersectionofthecontrollerandtheviewisrepresentedbytheJSFcomponenttreewhichcouldbeboundtoabackingbeanviathecomponent’sbindingattribute.TheintersectionoftheviewandthemodelisrepresentedbypropertygettersandsettersofEL(ExpressionLanguage)valueexpressionswhichcouldbeboundtoabackingbean,usuallyviathecomponent’svalueattribute.TheintersectionofthecontrollerandthemodelisrepresentedbyactionmethodinvocationsofELmethodexpressionswhichcouldbeboundtoabackingbeanviathecomponent’sactionattribute.Finally,theintersectionofallintersectionsisrepresentedbythebackingbeanitself.

Page 390: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure8-1 ThepositionofthebackingbeaninJSF’sMVCparadigm

InthisMVCparadigmthebackingbeanhasthusaratheruniqueposition.Notethatthebackingbeandoesn’tnecessarilyneedtoberepresentedbyasingleclass.Itcanevenberepresentedbymultipleclasses,eachwithitsownmanagedbeanscope,liketheviewcanberepresentedbymultipleFaceletsfilesandthemodelcanberepresentedbymultipleEJB/JPAclasses.

ComingbacktotheJSFdeveloper’spointofview,wecanevengetastepfurtherwithconsideringwhetherthebackingbeanisamodeloracontroller,dependingonhowyoucodethebackingbeanclass.Followingisoneway:@Named@RequestScoped@Stateful

publicclassProductBacking{

privateStringproductName;

privateStringproductDescription;

Page 391: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Inject

privateActiveUseractiveUser;

@PersistenceContext

privateEntityManagerentityManager;

publicvoidsave(){

Productproduct=newProduct();

product.setName(productName);

product.setDescription(productDescription);

product.setCreatedBy(activeUser.get());

entityManager.persist(product);

FacesContext.getCurrentInstance().addMessage(nul

l,

newFacesMessage("Productcreated!"));

}

//Add/generategettersandsettersforproductname

anddescription.

}

Inthisrathernaïveway,theentity’spropertiesareessentiallyduplicatedinthebackingbeanclassandthebusinesslogicistightlycoupledinthebackingbeanclass.Inotherwords,thebackingbeanclasshasincorrectlytakenovertheresponsibilitiesoftherealmodel.Onewouldmisinterpretsuchbackingbeanclassasbeingthesolemodel.Whenweeliminatethisduplicationandunreusability,wefindanotherway:@Named@RequestScopedpublicclassProductBacking{

Page 392: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

privateProductproduct=newProduct();

@Inject

privateProductServiceproductService;

publicvoidsave(){

productService.create(product);

FacesContext.getCurrentInstance().addMessage(nul

l,

newFacesMessage("Productcreated!"));

}

publicProductgetProduct(){

returnproduct;

}

}

wherebytheProductServicelooksasfollows:@Stateless

publicclassProductService{

@PersistenceContext

privateEntityManagerentityManager;

@Inject

privateActiveUseractiveUser;

publicLongcreate(Productproduct){

product.setCreatedBy(activeUser.get());

entityManager.persist(product);

returnproduct.getId();

Page 393: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

}

Thisisactuallythecorrectwayofauthoringbackingbeans.Whencomparingittothefirstway,youcouldarguethatthebackingbeanhas,intheJSFdeveloper’spointofview,becomeacontrollerfortheEJB/JPAmodel.ThebackingbeanbeingacontrollerisnotwrongfromtheJSFdeveloper’spointofview,butthisisnotactuallycorrectfromtheJSFframework’spointofviewwheretheFacesServletistherealcontroller.TheFacesServlettreatsthebackingbeanasamodel,becausetheFacesServletdoesn’thavedirectaccesstotherealmodel,theservicelayer.YouastheJSFdevelopercan,ofcourse,inyourcontexttreatthebackingbeanasacontroller,becauseyoucaneasilyignorealldutiesoftheFacesServletasyoudon’tneedtoworryaboutitsjobwhilewritingJSFcode.AllyouneedtoworryaboutwhilewritingJSFcodeistheview,themodel,andthebackingbean.TherestisdonetransparentlybyJSF.

ManagedBeansTheconceptualdifferencebetweena“backingbean”anda“managedbean”canberepresentedbythefollowinglinesofcodeexecutedunderthehoodofthebeanmanagementfacility:BackingBeanClassmanagedBeanInstance=newBackingBeanClass();

someContext.put("managedBeanName",managedBeanInstance,

someScope);

Inotherwords,thebackingbeanistheconcreteclasscreatedbyyou,theJSFdeveloper,andregisteredintosomebean

Page 394: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

managementfacility,suchasCDI.Thebeanmanagementfacilitywillautomaticallymanagethebean’slifecyclebyperformingconstruction,dependencyinjection,anddestructionwhennecessary,withoutyouhavingtodoitmanually.Ifyou’veeverdevelopedwithJSP/Servlets,thisbasicallyremovestheneedtomanuallyinstantiatebeansandputthemasanattributeoftheServletContext,HttpSession,orServletRequest. ToregisterabackingbeanclassasaCDImanagedbeanforJSFviews,[email protected] ontheclasssignature.

@Named

publicclassBackingBeanClass{

//...

}

ItwillthenimmediatelybeavailableinELcontextby#{backingBeanClass}andinallothermanagedbeansvia@Inject.TheELcontextisdirectlyavailableinFaceletsfiles.Bydefault,themanagedbeannameisderivedfromthebackingbean’sclassnamebylowercasingthefirstcharacter.Thiscanoptionallybeoverriddenbyspecifyingthevalueofthe@Namedannotation.

@Named("managedBeanName")

publicclassBackingBeanClass{

//...

}

ThisisnowinELcontextavailableby#{managedBeanName}.Nothinghaschangedforthe

2

3

Page 395: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Injectapproach.OnceaJSFbackingbeanbecomesamanagedbean,itwillbeautomaticallyinstantiatedandinitializedwheneverit’saccessedforthefirsttimeinthecontextassociatedwiththebean’sscope.Itwillbeautomaticallydestroyedwhenthelifecycleassociatedwiththebean’sscopehasended.Moreaboutmanagedbeanscopesinthenextsection.

Historically,JSFprovidedanativewaytoregisterbackingbeanclassesasmanagedbeans:first,inJSF1.xvia<managed-bean>entriesinfaces-config.xml,[email protected],whichis,sinceJSF2.3,officiallydeprecatedinfavorofCDI@Named.CDIwasintroducedforfirsttimeinJavaEE6,atthesametimeasJSF2.0,withtheaimofunifyingthemanagementofcontext-sensitiveinstancesandinjectingthecurrentlyavailableinstancesineachother.Unfortunately,theJSF2.0@ManagedBeanwasalreadysetinstonelongbeforeCDIwasfinished,sothosetwowaysofmanagingbeansdidexistinparallelforsometime.TheCDIbeanmanagementfacilityhasseveraladvantagesontopofJSFbeanmanagementfacility.

First,injectingonemanagedbeaninanothermanagedbeanusingCDI’s@Injectdoesn’trequireagetter/setterpairintheparentbackingbeanclass,whileJSF’[email protected]/setterpair,whichisconsideredpoorpracticeasthisexposestoomuchinformationtotheoutside,whichispotentiallyconfusing.Shouldweaccesstheinjectedbeanvia#{bean}or#{parentBean.bean}?

Second,theinjectedCDImanagedbeancanbeofa

Page 396: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

narrowerscopethantheparentmanagedbean.ThisispossiblebecauseCDI@Injectactuallyinjectsaproxyinstancewhichinturndelegatestothecurrentlyavailableinstance,whileJSF@ManagedProperty“injects”theactualinstancebyinvokingthesettermethoddirectlyaftertheconstructionoftheparentbean.

Third,CDImanagedbeansareaccessibleinallotherJavaEEartifactswhicharenotdirectlymanagedbyJSF,suchaswebservlets,webfilters,weblisteners,socketendpoints,webserviceendpoints,enterprisebeans,etc.Thisallowsaveryeasywayofexchangingdataacrossvariouslayerswithinthesameapplication,particularlywithinthesameHTTPsession.

Onceagain,theJSFbeanmanagementfacilityisofficiallydeprecatedsinceJSF2.3.YoushouldabsolutelynotuseitanymoreinnewJSFapplications.ItwillstillbethereintheJSFAPIforbackwardcompatibility,butchancesarethatthejavax.faces.beanpackagewillberemovedaltogetherinafutureJSFversion.ExistingJSFapplicationsshouldbemigratedtoCDIassoonaspossible.CDIisnativelyavailableinnormalJavaEEapplicationserversandrelativelyeasytoinstallinbarebonesservletcontainers.Forexample,JBossWeld,oneoftheCDIimplementations,canalreadybeinstalledinTomcatbysimplyaddingthesingledependencyorg.jboss.weld.servlet:weld-servlet-shaded

totheproject withoutanyfurthereffort.

ScopesThemanagedbeanscopebasicallyrepresentsthelifetimeofthemanagedbean.Ashintedintheprevioussection,inplainJSP/Servletperspectivethescopesarerepresentedbythe

4

Page 397: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

objectbeingputasanattributeoftheServletContext,HttpSession,orServletRequest.Thoseobjectswillthenbecomeapplicationscoped,sessionscoped,andrequestscoped,respectively.ThisisstillhowitworkswithCDIandall;itonlyaddsanextraabstractlayeroveritsothatyoudon’tanymoreneedtomanuallycreateandputtheobjectsinacertainscope.

InstandardJSF,thefollowingCDImanagedbeanscopesareavailableforJSFbackingbeans,orderedfromthelongestlivingtotheshortestliving.

1. @javax.enterprise.context.ApplicationScoped

2. @javax.enterprise.context.SessionScoped

3. @javax.enterprise.context.ConversationScoped

4. @javax.faces.flow.FlowScoped

5. @javax.faces.view.ViewScoped

6. @javax.enterprise.context.RequestScoped

7. @javax.enterprise.context.Dependent

Notethatthejavax.faces.beanpackagealsodefinesasetofscopes,buttheyareonlyapplicableonthebeansmanagedbyJSF’s@ManagedBean,notonbeansmanagedbyCDI.Moreover,thejavax.faces.beanpackageisdeprecatedsinceJSF2.3infavorofCDI.

@APPLICATIONSCOPEDAnapplication-scopedmanagedbeaninstanceistiedtothelifetimeofthewebapplicationitself.It’sunderthehoodrepresentedasanattributeoftheServletContextwhichiscreatedonthewebapplication’sdeploymentanddestroyed

Page 398: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

onthewebapplication’sundeployment.Notethatthisisnotequaltotheserver’sstartupandshutdown.Webapplicationscanbedeployedandundeployedonarunningserver.

Inotherwords,there’sonlyoneinstanceofanapplication-scopedmanagedbeanthroughoutthewebapplication’slifetimewhichissharedacrossallrequestsandsessions.Youcouldarguethatitbehaveslikeasingleton.However,itdoesn’tactuallyfollowthesingletondesignpattern.Itfollowsthe“justcreateone”designpattern. Arealsingletondoesn’thaveanypublicconstructorbutonlyastaticmethodwhichreturnsastaticallyinitializedlazyloadedinstance.ArealJavaBean,ontheotherhand,requiresthepresenceofadefaultconstructor.

Bydefault,anapplication-scopedbeanmanagedinstanceiscreatedforthefirsttimewhenthewebapplication’scodeaccessesitforthefirsttimeduringthewebapplication’slifetime.It’sthusnot,perdefinition,immediatelycreatedwhentheServletContextinstanceiscreated.Itis,however,guaranteedwhentheServletContextinstanceisdestroyed.

Application-scopedmanagedbeansareusefulforapplication-widedatawhichneedstobeinitializedonlyonceintheapplication’slifetime,orneedstoprovidenon-staticgetterswhichdelegatetostaticvariables,orneedstoprovidefunctionsforusageinEL.ThefollowingexamplemakessurethatapplicationsettingsstoredinthedatabaseareloadedonlyonceandprovidedasaMapby#{settings}duringtherestoftheapplication’slifetime.

@ApplicationScoped

publicclassApplicationSettingsProducer{

5

Page 399: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

privateMap<String,String>settings;

@Inject

privateApplicationSettingsService

applicationSettingsService;

@PostConstruct

publicvoidinit(){

settings=applicationSettingsService.getAll();

}

@Produces@Named

publicMap<String,String>getSettings(){

returnsettings;

}

}

Notethatthe@Namedannotationisplacedonthegetter,whichimpliesamanagedbeannamematchingthepropertyname:#{settings}.Alsonotethatthegetterinturnneedsthe@Producesannotationinordertoberecognizedasamanagedbeanproducer.Followingisanotherexamplewhichofferstextformattingfunctions.

@Named@ApplicationScoped

publicclassFormat{

publicStringdate(LocalDatelocalDate){

if(localDate!=null){

return

localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);

}

else{

return"n/a";

}

}

Page 400: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicStringcurrency(BigDecimalamount){

if(amount!=null){

return

NumberFormat.getCurrencyInstance(Locale.US)

.format(amount);

}

else{

return"n/a";

}

}

}

Thiscouldbeusefulin,forexample,adatatabletokeeptheFaceletscodeterse.

<h:dataTablevalue="#{cart.products}"var="product">

<h:column>#{format.date(product.lastModified)}</h:column>

<h:column>#{format.currency(product.discount)}</h:column>

</h:dataTable>

It’salsousefulincaseyouneedaformattedvalueinanattributewhichdoesn’tallowanested<h:outputText><f:convertXxx>.

<h:commandLink...title="Lastvisited#

{format.date(user.lastVisited)}">

@SESSIONSCOPEDAsession-scopedmanagedbeaninstanceistiedtothelifetimeoftheestablishedHTTPsession.It’sunderthehoodrepresentedasanattributeoftheHttpSessionwhichiscreatedforeveryuniqueclientondemandofthewebapplication’scode.Whenthewebapplication’scodedirectly

Page 401: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

orindirectlypokestheHttpSessionforthefirsttimeviaHttpServletRequest#getSession(),theservletcontainerwillcreateanewHttpSessioninstance,generatealonganduniqueID,andstoreitinserver’smemory.TheservletcontainerwillalsosetasessioncookieontheHTTPresponsewith“JSESSIONID”asthecookienameandtheuniquesessionIDasthecookievalue.A“sessioncookie”isidentifiedbytheabsenceofthe“maximumage”attribute.

AspertheHTTPcookiespecification,theclient(thewebbrowser)isrequiredtosendthiscookiebackintheheaderofthesubsequentrequestsaslongasthecookieisvalid.Inanydecentwebbrowseryoucaninspecttherequestandresponseheadersinthe“Network”sectionofthewebdeveloper’stoolsetwhichisaccessiblebypressingF12inthewebbrowser.TheservletcontainerwillcheckeveryincomingHTTPrequestforthepresenceofthecookiewiththename“JSESSIONID”anduseitsvalue(thesessionID)togettheassociatedHttpSessioninstancefromserver’smemory.

Ontheserverside,theHttpSessioninstancestaysaliveuntilithasnotbeenaccessedformorethanthetimeoutvalueasspecifiedinthe<session-timeout>settingofweb.xml,whichdefaultsto30minutesonmostifnotallservletcontainers.So,whentheclientdoesn’tvisitthewebapplicationforlongerthanthetimespecified,theservletcontainerwilldestroytheHttpSessioninstance.EverysubsequentHTTPrequest,evenwiththecookiespecified,willnothaveaccesstotheHttpSessioninstanceanymore;theservletcontainerwillcreateanewHttpSessioninstanceandoverwritethecookievaluewiththenewsessionID.

Ontheclientside,bydefault,allsessioncookiesstayalive

Page 402: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

foraslongasthebrowserinstanceisrunning.So,whentheclientshutdownsthebrowserinstance,allsessioncookiesaredestroyedontheclientside.Inanewbrowserinstance,thesessioncookiesfromapreviousbrowsersessionarenotavailableanymore,sothebrowserwon’tsendanyJSESSIONIDcookie.Theserverwilltheninterpretitasabrand-newsession.TheHttpSessioninstanceassociatedwiththepreviousbrowsersessionwillsilentlyexpireontheserverside.

Bydefault,asession-scopedmanagedbeaninstanceiscreatedforthefirsttimewhenthewebapplication’scodeaccessesitforthefirsttimeduringtheHTTPsession’slifetime.It’sthusnot,perdefinition,immediatelycreatedwhentheHttpSessioninstanceiscreated.Itis,however,guaranteedtobedestroyedwhentheHttpSessioninstanceisdestroyed.Session-scopedmanagedbeansareeffectivelysharedacrossallbrowsertabswithinthesamebrowsersession.

Session-scopedmanagedbeansareusefulforkeepingtrackofclient-specificdata,suchastheentityrepresentingthecurrentlylogged-inuser,theselectedlanguage,andotheruser-relatedpreferences.Thefollowingexamplecalculatesthecurrentlocaleandprovidesagetter/setterforitsothatitcanbeobtainedintheviewandmodifiedbyanUIInputcomponent.

@Named@SessionScoped

publicclassActiveLocaleimplementsSerializable{

privateLocalecurrent;

@PostConstruct

Page 403: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicvoidinit(){

FacesContextcontext=

FacesContext.getCurrentInstance();

current=context.getApplication()

.getViewHandler().calculateLocale(context);

}

//Getter+setter.

}

Amoreelaborateexamplecanbefoundinthesection“ChangingtheActiveLocale”inChapter14.Donotethatsession-scopedmanagedbeansmustimplementSerializablebecausetheHttpSessioninstanceitself,wherethosebeansarebeingstored,issubjecttobeingwrittentodiskincaseofaserverrestartoreventobeingtransferredoverthenetworktoadifferentserverincaseofaserverclusterconfigurationwithadistributablesession.

Anotherclassicexampleisa“shoppingcart.”

@Named@SessionScoped

publicclassCartimplementsSerializable{

privateList<Product>products=newArrayList<>();

publicvoidaddProduct(Productproduct){

products.add(product);

}

//...

}

@CONVERSATIONSCOPEDAconversation-scopedmanagedbeanistiedtothelifetimeof

Page 404: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

theinjectedjavax.enterprise.context.Conversation

instancewhichoffersbegin()andend()methodswhichmustbeexplicitlyinvokedbythewebapplication’scodeinordertoindicatethestartandendoftheconversationscope.TheconversationscopeisrepresentedbyapredefinedHTTPrequestparameterwithadefaultnameof“cid”(“ConversationID”)whosevaluereferencesrepresentstheconversationID.TheconversationIDinturnreferencesanisolatedmappinginthecurrentHTTPsessionwheretheconversation-scopedmanagedbeaninstanceswillbestored.

Aslongastheconversationscopehasnotstarted,theconversation-scopedmanagedbeanwillbehavelikearequest-scopedmanagedbean.WhentheapplicationcodeexplicitlyinvokesConversation#begin(),thentheconversationscopewillstartandacustomjavax.faces.application.ViewHandlerprovidedbytheCDIimplementationwillmakesurethatallitsgetXxxURL()methodssuchasgetActionURL()andgetBookmarkableURL()returnaURL(uniformresourcelocator)withtheconversationIDparameterincluded.IncaseofWeld,that’stheConversationAwareViewHandler.AllJSFUIFormandUIOutcomeTargetcomponentsderivetheiractionandtargetURLsfromthosemethodsoftheViewHandler.ThegeneratedHTMLoutputofthosecomponentswillthusultimatelyincludetheconversationIDinthetargetURL.

OnanincomingHTTPrequest,whentheconversationIDparameterispresentintherequest,anditisstillvalid,theCDIimplementationwillobtaintheassociatedconversationscope

6

Page 405: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

fromtheHTTPsessionandmakesurethatallconversation-scopedmanagedbeansareobtainedfromexactlythisconversationscopeidentifiedbytheconversationID.ThisworksonbothGETandPOSTrequests.Anyformsubmitoranylink/navigationtoaURLwiththeconversationIDincludedwillprovideaccesstotheverysameconversationscope,aslongasit’sstillvalid.TheconversationscopeendswhentheapplicationcodeexplicitlyinvokesConversation#end().Whentheenduserreusesthe“cid”requestparameterlater,ormanipulatesitsvaluetoonewhichisn’tstartedinitsownbrowsersession,orwhentheunderlyingHttpSessioninstanceisdestroyed,thenCDIwillthrowajavax.enterprise.context.NonexistentConver

sationException.Conversation-scopedmanagedbeansareparticularly

usefulinordertobeabletoreturntoaparticularstatefulpagewithinthesamebrowsersessionafterbeingredirectedelsewhere.Aclassicexampleisathird-partywebservicewhichisincludedinanHTML<iframe>oropenedinanewbrowsertaboreventargetedintheactionattributeofaplainHTML<form>,andcan,viaaspecificrequestparameter,beconfiguredtoredirectbacktoyourwebapplicationaftercompletingtheservice.WhenyouincludetheconversationIDintheredirectURL,thenyouwillintheredirectedpagebeabletoresumewithexactlythesameconversation-scopedmanagedbeaninstanceasitwasbeforetheredirect.Thisallowsyoutheopportunitytofinalizeandunlockanypendingtransactionsand,ofcourse,endtheconversation.

Page 406: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Givenacheckoutbuttonwhichlooksasfollows,

<h:form>

...

<ui:fragmentrendered="#{emptypayment.url}">

...

<h:commandButtonvalue="Checkout"action="#

{payment.checkout}">

<f:ajaxrender="@form"/>

</h:commandButton>

</ui:fragment>

<ui:fragmentrendered="#{notemptypayment.url}">

<iframesrc="#{payment.url}"></iframe>

</ui:fragment>

</h:form>

here’swhattheassociatedconversation-scopedbeanbehind#{payment}lookslike:@Named@ConversationScopedpublicclassPaymentimplementsSerializable{

privateOrderorder;

privateStringurl;

@Inject

privateCartcart;

@Inject

privateOrderServiceorderService;

@Inject

privateConversationconversation;

publicvoidcheckout(){

Page 407: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

conversation.begin();

order=

orderService.lockProductsAndPrepareOrder(cart);

url="http://third.party.com/pay?returnurl="

+

URLEncoder.encode("http://my.site.com/paid?cid="

+conversation.getId(),"UTF-8");

}

publicvoidconfirm(){

orderService.saveOrderAndCreateInvoice(order);

conversation.end();

}

@PreDestroy

publicvoiddestroy(){

orderService.unlockProductsIfNecessary(order);

}

publicStringgetUrl(){

returnurl;

}

}

Basically,thecheckoutbuttonisonlyrenderedwhenthere’snopaymentURLset.Oncethebuttonispressed,allproductsoftheshoppingcartarelockedandtheorderisprepared.Also,dependingonthethird-partypaymentservice,theURLreferringitmustbepreparedwherebyyouincludethereturnURLassomequeryparameterintheURLofthepaymentservice.ThereturnURLshouldinturnincludethe“cid”

Page 408: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

requestparameterrepresentingtheconversationID.Intheredirectedpagewhichwillactuallybeloadedinthe<iframe>,youcanjustmarktheconversationcompletewith<f:viewAction>.

<f:metadata>

<f:viewActionaction="#{payment.confirm}"/>

</f:metadata>

Ofcourse,theaveragethird-partypaymentserviceshouldhaveamoreelaborateJavaorevenJavaScriptAPIinsteadof<iframe>;also,itshouldbepossibletoprovidedifferentreturnpagesforeachpaymentoutcomesuchaspaymentfailedandpaymentaborted.Theaboveexampleisjusttogivethegeneralidea.

@FLOWSCOPEDAflow-scopedmanagedbeanistiedtothelifetimeofaJSFflow.Itusesthesameprincipleasconversationscope,onlytheconversationisfurthernarroweddowntoaspecificsetofJSFviewsinanisolatedsubfolder.OncetheenduserclicksaJSFlinkorbuttoncomponentwhichnavigatestoaspecificentrypageofaJSFflow,thentheflowscopewillautomaticallystart.TheflowscopecannotbestartedwhenyouopentheentrypagewithoutnavigatingviaaJSFcomponent.Thatis,JSFwill,withhelpoftheViewHandler,automaticallyappendthepredefinedHTTPrequestparameter“jfwid”(“javax.facesWindowID”)totheoutcomeURLwhosevaluerepresentstheJSFclientwindowID.TheJSFclientwindowIDinturnreferencesanisolatedmappinginthecurrentHTTPsessionwheretheflow-scopedmanagedbeansarestored.

Page 409: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Additionally,particularlywhenusingaUIOutcomeTargetcomponentinsteadofaUICommandcomponenttonavigate,thequerystringmaybeaccompaniedwith“jffi”(javax.facesFlowID)and“jftfdi”(javax.facesToFlowDocumentID)requestparameters.ThoseareactuallyonlymandatoryforstartingaJSFflowusingaGETrequest.Technically,fortherestoftheJSFflow,“jfwid”issufficient.Aslongasthe“jfwid”parameterispresent,andisstillvalid,thentheJSFflowisidempotentandcanberesumedusingaGETrequest.WhenyouopenanewbrowsertabandnavigateintotheJSFflow,thenactuallyanewflowscopewillbestarted,independentoftheJSFflowinothertab.OnceapostbackrequestwithintheJSFflownavigatestoapageoutsidetheJSFflow,thentheflowscopewillautomaticallyend.Whentheenduserreusesthe“jfwid”requestparameterlater,ormanipulatesitsvaluetoonewhichisn’tstartedinitsownbrowsersession,orenterstheflowdirectly,orwhentheunderlyingHttpSessioninstanceisdestroyed,thenCDIwillthrowajavax.enterprise.context.ContextNotActiveE

xception.Themajordifferencebetweentheflowscopeandthe

conversationscopeisthusthatthepageswithinaJSFflowcannotbeentereddirectly.TheywillautomaticallystartwhentheendusernavigatestotheentrypageoftheJSFflowandtheywillautomaticallyendwhenapostbacknavigatesoutsidetheJSFflow.Flow-scopedmanagedbeansareusefulinordertoisolateaconversationtoaspecificsetofJSFpages.Aclassicreal-worldexampleisabookingapplicationwhichisspreadovermultipleformsinphysicallydifferentpages.

Page 410: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

TherearevariouswaystodefineaJSFflow.Onewayisbyconvention,anotherwayisbydeclarativeconfigurationinthe[flowId][flowId]-flow.xmlfile,andyetanotherwayisbyprogrammaticconfigurationusingjavax.faces.flow.FlowBuilderAPI. Inthisbookwe’llrestrictourselvestoconventionoverconfiguration.First,createthefollowingfolderstructure:

Thefirstconventionisthattheflowentrypagemusthaveexactlythesamenameasthesubfolderitissittingin.Inthiscase,that’s“booking”.ThisisconsideredtheFlowID.Thesecondconventionisthattheremustbea*-flow.xmlfileinthesubfolderwhosenameisprefixedwiththeFlowID,i.e.,booking-flow.xml.ThisXMLconfigurationfilecanbekeptemptyfornow.It’sonlyusefulwhenyouwanttofine-graintheJSFflowconfiguration,e.g.,byspecifyingadifferententrypage.Withoutthisfile,theJSFflowscopewon’tbeactivated.OnedisadvantageofatleastoneactivatedJSFflowinthewebapplication,however,isthattheJSFclientWindowIDparameter“jfwid”willbeappendedtoevery

7

Page 411: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

singlenavigationURL,evenwhenitdoesn’ttargetaJSFflow.ThisURLpollutionmayforsomedevelopersbethemainreasontonotusetheJSFflowscopeatall.

ThenavigationcomponentinordertoenteraJSFflowmustbeplacedinaJSFpageoutsidetheflowsubfolder.Thenavigationoutcomemustreferencethesubfoldername,whichistheFlowID.Here’sanexamplein/home.xhtml.

<h:buttonvalue="Startbooking"outcome="booking"/>

Ofcourse,thiscanbesubstitutedwith<h:link>.It’srecommendedtouseGETjustforthissothatthebookingpage’sURLreflectsinthebrowser’saddressbar.Then,inallpageswithinthesubfolder,youcanreferenceaflow-scopedmanagedbeanwhichwillbesharedacrossallthesepages.Youcannavigatebackandforthbetweenthesepagesaswellwhileretainingtheflow-scopedmanagedbeaninstance.WerecommendthatyouuseAjaxwithredirectforthis.TheAjaxsubmitswillimprovetheuserexperience.Theredirectswillmakesurethattheindividualpagesarestillbookmarkable.

bookingbooking.xhtml:<h:form><h:inputTextvalue="#{booking.startDate}"/>

...

<h:commandButtonvalue="Next"action="persons?faces-

redirect=true">

<f:ajaxexecute="@form"render="@form"/>

</h:commandButton>

</h:form>

bookingpersons.xhtml:<h:form><ui:repeatvalue="#{booking.persons}"var="person">

Page 412: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:inputTextvalue="#{person.name}"/>

...

</ui:repeat>

...

<h:commandButtonvalue="Back"action="booking?faces-

redirect=true">

<f:ajaxexecute="@form"render="@form"/>

</h:commandButton>

<h:commandButtonvalue="Next"action="confirm?faces-

redirect=true">

<f:ajaxexecute="@form"render="@form"/>

</h:commandButton>

</h:form>

bookingconfirm.xhtml:<h:form><h:outputTextvalue="#{booking.startDate}"/>

...

<h:commandButtonvalue="Back"action="persons?faces-

redirect=true">

<f:ajaxexecute="@form"render="@form"/>

</h:commandButton>

<h:commandButtonvalue="Next"action="payment?faces-

redirect=true">

<f:ajaxexecute="@form"render="@form"/>

</h:commandButton>

</h:form>

bookingpayment.xhtml:<h:form><h:selectOneMenuvalue="#{booking.paymentMethod}">

...

</h:selectOneMenu

Page 413: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

...

<h:commandButtonvalue="Back"action="confirm?faces-

redirect=true">

<f:ajaxexecute="@form"render="@form"/>

</h:commandButton>

<h:commandButtonvalue="Submit"actionListener="#

{booking.submit()}"

action="/home?faces-redirect=true">

<f:ajaxexecute="@form"render="@form"/>

</h:commandButton>

</h:form>

And,finally,theflow-scopedbeanbehind#{booking}:

@Named@FlowScoped("booking")

publicclassBookingimplementsSerializable{

privateLocalDatestartDate;

privateList<Person>persons;

privatePaymentMethodpaymentMethod;

//...

publicvoidsubmit(){

//...

}

}

Yousee,themostofnavigationtaskisdonebytheactionattributeofthecommandcomponents.?faces-redirect=trueisaspecialrequestparameterwhichisinternallyrecognizedbyJSFasaninstructiontoperformaredirectafterpostbackandofcoursestripofffromthetargetURLbeforeperformingtheactualredirect.Oncethepostbackleavestheflow,theflow-scopedmanagedbeanisdestroyed

Page 414: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

andthepreviouslypresentedpageURLsarenotreusableanymore.

@VIEWSCOPEDAview-scopedmanagedbeanistiedtothelifetimeoftheJSFviewstate.TheJSFviewstateiselaboratedinthesection“ViewState”inChapter3.Inanutshell,aview-scopedmanagedbeanlivesaslongastheenduserisperformingpostbackrequestsontheverysameJSFviewandtheinvokedactionmethodskeepreturningnullorvoid.Oncetheactionmethodreturnsnon-null,evenifit’sanemptystringorrepresentsthesameview,thentheviewscopewillend.View-scopedmanagedbeansarenotsharedacrossbrowsertabswithinthesamebrowsersession.Eachonegetsitsownuniqueinstance.Effectively,theyareindirectlyidentifiedbythejavax.faces.ViewStatehiddeninputfieldinthegeneratedHTMLrepresentationofaJSFform.

However,view-scopedmanagedbeansarenotstoredintheJSFviewstate,notevenwhenclient-sidestatesavingisenabled.TheyareactuallystoredintheHTTPsession,regardlessoftheJSFstatesavingmethod.TheyarenotimmediatelydestroyedwhentheenduserunloadsthewebpageeitherbyperformingaGETrequestviaalinkorbookmarkoreditingtheURLinbrowser’saddressbar,orbyclosingthebrowsertab.TheywillstickaroundintheHTTPsessionandonlygetdestroyedwhentheHTTPsessionexpires.

Asanendusercanintheoryspawnanunlimitedamountofbrowsertabswithinthesamesession,andthusalsoasmanyJSFviewstatesandview-scopedmanagedbeans,there’sa

Page 415: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

configurablemaximumlimitontheamountofJSFviewstatesandview-scopedmanagedbeanswhichwillbestoredintheHTTPsession.Oncethislimitisreached,theleastrecentlyusedJSFviewstateandview-scopedmanagedbeanwillbeexpiredanddestroyed.WhentheenduseractuallygoesbacktothetabthatoriginallyreferencedthenowexpiredJSFviewstate,andperformsapostbackrequestonit,JSFwillthrowaViewExpiredException.ThelimitontheamountofJSFviewstatesisdependentontheJSFimplementationused.InMojarra,thislimitisconfigurablebythecom.sun.faces.numberOfLogicalViewscontextparameterinweb.xmlwhosedefaultvalueis15.

<context-param>

<param-name>com.sun.faces.numberOfLogicalViews</param-

name>

<param-value>25</param-value>

</context-param>

Ifyourwebapplication,however,invitesbeingopenedinmanymorebrowsertabs,suchasadiscussionforumoraQ&Asite,thenyou’dbetterswitchtoclient-sidestatesaving.ThiswaytheJSFviewstatesarenolongerstoredintheHTTPsessionandwillthereforealsoneverexpire.However,theassociatedview-scopedmanagedbeansarestillstoredintheHTTPsessionandexpirable.Whentheenduseractuallygoesbacktothetabthatoriginallyreferencedthenowexpiredviewscoped-managedbean,andperformsapostbackrequestonit,JSFwillnotthrowaViewExpiredExceptionbutinsteadwillcreateanewonefromscratch,therebylosingallthestatechangestotheoriginalmanagedbeaninstance.Thelimiton

8

Page 416: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

theamountofview-scopedmanagedbeansisalsodependentontheJSFimplementationused.InMojarra,thislimitisnotyetconfigurablebyaweb.xmlcontextparameter.It’sonlyconfigurablebyexplicitlysettingthesessionattributewiththenamecom.sun.faces.application.view.activeViewM

apsSizewhosedefaultvalueis25.Thiscanbeachievedwithanapplication-scopedmanagedbeanasfollows,whichobservestheinitializationofthesessionscope.

@ApplicationScoped

publicclassConfig{

publicvoidsessionCreated

(@Observes@Initialized(SessionScoped.class)

HttpSessionsession)

{

session.setAttribute

("com.sun.faces.application.view.activeViewMapsSi

ze",15);

}

}

ThisconfigurationactuallydecreasesthedefaultvaluetobeequaltodefaultmaximumamountofJSFviewstatesinsession.Thisisfinewhenyou’reusingserver-sidestatesavingandallyourJSFviewseffectivelyreferenceonlyoneview-scopedmanagedbeaninstance.However,inadecentlydevelopedandrefactoredJSFwebapplication,theaverageJSFpageusuallyreferencesmultipleview-scopedmanagedbeans.Ifyouhave,forexample,amaximumamountofthreedifferentview-scopedmanagedbeansperJSFview,thenyou’dbestsetthelimittothreetimesthevalueof

Page 417: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

com.sun.faces.numberOfLogicalViews.Youonlyneedtotakeintoaccountthepossiblememoryconsumption.Itwillquicklygooverboardwhentheview-scopedmanagedbeansinturnhold,relatively,alotofdata.

View-scopedmanagedbeansareveryusefulforretainingstateacrossAjax-basedpostbacksonthesameJSFview,particularlyifthosepostbacksresultinchangesinthevalueoftherenderedattributeofanyUIComponent,orthedisabledorreadonlyattributeofanUIInputcomponent,orthedisabledattributeofanyUICommandcomponentwithinthesameJSFview.Thatis,onasubsequentpostback,JSFwill,aspartofasafeguardagainstatamperedrequest,recheckthembeforeactuallyprocessingthecomponent.Ifthemanagedbeanholdingthestatewasrequestscopedinsteadofviewscoped,thenthosechangesintheconditionswouldgetlostinasubsequentpostbackandthepostbackwouldn’tgetprocessedasintuitivelyexpected.Inotherwords,view-scopedmanagedbeansareparticularlyusefulindynamicforms.

Oneexampleisadrop-downwhichconditionallyrendersafreetextfieldwhenthe“other”optionischosen.

<f:metadata>

<f:importConstantstype="com.example.project.model.Title"

/>

</f:metadata>

...

<h:form>

<h:selectOneMenuvalue="#{bean.customer.title}">

<f:selectItemsvalue="#{Title}"/>

<f:ajaxrender="other"/>

</h:selectOneMenu>

<h:panelGroupid="other">

Page 418: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:inputTextrendered="#{bean.customer.titleeq

'OTHER'}"

value="#{bean.customer.titleOther}"

required="true"/>

</h:panelGroup>

...

<h:commandButtonvalue="Save"action="#{bean.save}"/>

</h:form>

Thisconstructwon’tworkwhenthemanagedbeanisrequestscopedandisthusrecreatedoneveryrequest.Whenthedrop-downchanges,itcreatesanewinstanceoftherequest-scopedbean,setsthetitlethere,andrendersthefreetextfieldandfinallytherequest-scopedbeaninstancegetsdestroyed.Whentheformsubmits,itcreatesanewinstanceoftherequest-scopedbean,thuswithoutthecustomertitle,andwhenJSFcheckstherenderedattributeduringtheapplyrequestvaluesphase(secondphase)ofthefreetextfieldandnoticesit’sfalse,ultimatelyitwon’tprocessthefreetextfieldatall.Itwillonlyworkwhenthemanagedbeanisviewscopedbecausethecustomertitlesetduringthedrop-downchangeisstillavailableduringtheapplyrequestvaluesphase(secondphase)oftheformsubmit.

Thereis,however,awork-around.YoucouldlettherenderedattributechecktheHTTPrequestparameterinsteadofthemodelvalue.AsexplainedinChapter4,theHTTPrequestparameternameisspecifiedbythecomponent’sclientID.Youcouldbindthedrop-downcomponenttotheviewandthenuseitsclientIDtoobtaintheHTTPrequestparametervalue.

<h:selectOneMenubinding="#{title}"value="#

{bean.customer.title}">

Page 419: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:selectItemsvalue="#{Title}"/>

<f:ajaxrender="other"/>

</h:selectOneMenu>

<h:panelGroupid="other">

<h:inputTextrendered="#{param[title.clientId]eq

'OTHER'}"

value="#{bean.customer.titleOther}"required="true"

/>

</h:panelGroup>

Thisway,whenJSFcheckstherenderedattributeduringtheapplyrequestvaluesphase(secondphase)oftheformsubmit,itwillnoticeit’strueandcontinueprocessingthefreetextfield,evenwhenthemanagedbeanisrequestscopedandthus#{bean.customer.title}isstillnullatthatpoint.Notethatthebindingattributedoesn’treferenceamanagedbeanproperty.Thisisunnecessaryasitwouldn’tbeusedoverthere.AllofthisisalsoapplicableonthereadonlyattributeofanyUIInputcomponentandthedisabledattributeofanyUIInputandUICommandcomponent.

Theremayalsobecaseswhereinarequest-scopedmanagedbeanwillworkjustfinebutimposesariskofacorruptedstateascomparedtoaview-scopedmanagedbean,certainlywhenrelyingondatacomingfromashareddatabasewhichcouldbemutatedbyotherusers.ThisaffectsprimarilyusecaseswherebyUIInputorUICommandcomponentsarenestedinaniteratingcomponentsuchas<h:dataTable>,<ui:repeat>and<c:forEach>whichiteratesoveramodelcomingfromthedatabase.ThiswasexplainedpreviouslyinChapter6,butforthesakeofrefreshment,wewillexplainitoncemore.Imagineatableofproductswithadeletebuttoninacolumn.

Page 420: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:formid="list">

<h:dataTableid="products"value="#{products.list}"

var="product">

...

<h:column>

<h:commandButtonid="delete"value="Delete"

action="#{products.delete(product)}">

<f:ajaxrender="@namingcontainer"/>

</h:commandButton>

</h:column>

</h:dataTable>

</h:form>

Withthisbackingbean:

@Named@ViewScoped

publicclassProductsimplementsSerializable{

privateList<Product>list;

@Inject

privateProductServiceproductService;

@PostConstruct

publicvoidinit(){

list=productService.list();

}

publicvoiddelete(Productproduct){

productService.delete(product);

list.remove(product);

}

publicList<Product>getList(){

returnlist;

}

}

Page 421: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Thesubmittedbuttonisunderthehoodidentifiedbytheiterationindex.WhenJSFisabouttoprocesstheformsubmit,andaproducthasbeenaddedorremovedorevenreorderedinthemeanwhile,causingtheiterationindextobechanged,thentheinvokedactionwouldpossiblybeperformedagainstthewrongitemcurrentlyattheinitiallyknownindex.Thisisdangerousfortheintegrityofthemodel.Insuchacasethevalueoftheiterationcomponentmustreferaview-scopedmodel.

Alsohere,thereisawork-around.Insteadofrelyingontheiterationindex,youcanalsorelyontheuniqueidentifieroftheiteratedobjectwhichmustbepassedasanHTTPrequestparameterinsteadofasanELmethodargument.

<f:formid="list">

<h:dataTableid="products"value="#{products.list}"

var="product">

...

<h:column>

<h:commandButtonid="delete"value="Delete"

action="#{products.delete}">

<f:paramname="id"value="#{product.id}"/>

<f:ajaxrender="@namingcontainer"/>

</h:commandButton>

</h:column>

</h:dataTable>

</h:form>

Wherebythebackingbeanisadjustedasfollows:

@Named@RequestScoped

publicclassProducts{

privateList<Product>list;

Page 422: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Inject@ManagedProperty("#{param.id}")

privateLongid;

@Inject

privateProductServiceproductService;

@PostConstruct

publicvoidinit(){

list=productService.list();

}

publicvoiddelete(){

productService.delete(id);

list.removeIf(product->product.getId().equals(id));

}

publicList<Product>getList(){

returnlist;

}

}

No,theaction="#{products.delete(product.id)}"insteadofusing<f:param>won’twork.Thetechnicalreasonisthat<f:param>isexecutedimmediatelyduringtherenderresponsephaseoftheform,longbeforetheenduserpressesthedeletebutton.Thus,atthemomenttheenduserpressesthedeletebutton,it’sguaranteedtohavethecorrectvalue.TheELmethodargument,onthecontrary,isonlyevaluatedaftertheenduserhaspressedthedeletebutton.Whenthemodelhaschangedinthemeanwhile,itwouldthusevaluatetothewrongIDwhentheiterationindexoftheparticularproducthaschanged.

Asexplainedinthebeginningofthissection,thestandardJSFview-scopedbeanmanagementfacilityhasthustwomajor

Page 423: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

disadvantages:first,theinstancesdon’timmediatelyexpirewhentheenduserunloadsthewebpageandstickaroundintheHTTPsession;second,evenwithclient-sidestatesavingenabledtheyarestoredintheHTTPsession.ThoseproblemsarecurrentlynotyetsolvedinthestandardJSFAPI.

Fornow,theJSFutilitylibraryOmniFacesoffersanenhanced@ViewScopedannotationwhichsolvesthosetwodisadvantages. View-scopedmanagedbeansannotatedwith@org.omnifaces.cdi.ViewScopedwillactuallygetdestroyedwhentheenduserunloadsthepage.ThisisunderthehooddonewithhelpofNavigator.sendBeaconAPI inJavaScript,andaspecializedViewHandlerimplementationprovidedbyOmniFaceswhichlistensonthoseunloadrequests.Therehavebeenproductionapplicationsmakingheavyuseofview-scopedmanagedbeanswherebythememoryusagehasreducedforupto80%afterswitchingfromstandardJSF@ViewScopedtoOmniFaces@ViewScoped.Thismakesthedestroy-on-unloadfeatureamajorcandidatetobeaddedtothefutureversionofthestandardJSFAPI.

Inordertosavethephysicalview-scopedmanagedbeanintheJSFviewstatewhenclient-sidestatesavingisenabled,thesaveInViewStateattributeoftheOmniFaces@ViewScopedannotationmustbesettotrue.Youonlyneedtokeepinmindthatthosebeanswillneverexpire,notevenwhenthepagegetsunloaded,orwhentheHTTPsessionexpires.Infact,theentirebeanhasphysicallybecomepartofthegeneratedHTMLoutput,inthejavax.faces.ViewStatehiddeninputfield.TherehavebeencommunityrequeststomakeJSFstatemanagementmoreflexible,suchastogglingbetweenclient-andserver-sidestate

9

10

Page 424: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

savingonaper-view(perUIViewRoot)orevenperform(perUIForm)basis,andbeingabletostoreview-scopedmanagedbeansintheactualviewstateinsteadofintheHTTPsession.ThismayalsobereconsideredinafutureversionofthestandardJSFAPI.

@REQUESTSCOPEDArequest-scopedmanagedbeanisamongotherstiedtothelifetimeoftheHTTPrequest,whichisforJSFthemostimportantcase.OthercasesincludethelifetimesofacalltoanEJBasynchronousmethodinvocation(methodannotatedby@Asynchronous),anEJBtimertimeoutmethod,orwhenamessage-drivenbean(MDB)processesamessage.NotethatanAjaxrequestalsocountsasasingleHTTPrequest.

WhentheclientsendsanHTTPrequesttotheserver,theservletcontainerwillcreateHttpServletRequestandHttpServletResponseinstancesrepresentingtheHTTPrequestandresponse,andpassthemthroughtheauthenticationmodules,filters,andservlets.Theywillbedestroyedimmediatelyafterallauthenticationmodules,filters,andservletsarefinishedprocessingtherequestandresponse.Inotherwords,everyHTTPrequestcreatesanewinstanceofarequest-scopedmanagedbeanwhichisavailableonlyduringthatrequestandnotinotherrequests.

Request-scopedmanagedbeansareusefulforsimpleandstaticformswhichdon’thaveanydynamicAjax-basedupdates,forwhichyouwouldratheruseaview-scopedmanagedbean.Thinkofaloginformoracontactform.

@Named@RequestScoped

Page 425: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicclassLogin{

privateStringusername;

privateStringpassword;

//...

}

Sure,thoseformscanbetiedtoaview-scopedmanagedbeanaswellwithoutproblems,butthat’sawasteofmemoryspace.NotethatyoushouldabsolutelynotmaketheJPAentityitselfamanagedbean.Inotherwords,thefollowingapproachisfactuallywrong:@Named@RequestScoped@Entity

publicclassProduct{

//...

}

NotonlydoesthisviolatetheLawofDemeter, butitalsorisksthatJPAwon’tbeabletopersistit,becauseCDIactuallywrapsthemanagedbeaninaproxyclassandJPAwouldthennotbeabletoobtaintheentityinformationfromitwhenyou’reabouttopassaninjectedinstancetoJPA.Hibernatewouldinsuchcasethrow“Unknownentity:com.example.Entity$Proxy$_$$_WeldClientPro

xy”,whichthusactuallyrepresentstheCDIproxyclass.YoumightatthispointwonderhowexactlyCDIactually

works.First,itwillduringthewebapplication’sstartupcollectallclassesthatareannotatedwithaCDI-compatiblescopeannotation.Then,itwillgenerateproxyclassesforallofthem.Ultimatelyinstancesofthoseproxyclassesarebeinginjected.Givenanexamplebeanclasscom.example.Bean,the

11

Page 426: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

generatedCDIproxyclassmaylookasfollows:publicclassBean$Proxy$_$$_CDIextendsBeanimplementsSerializable

{

publicStringgetSomeProperty(){

BeanactualInstance=CDI.resolveItSomehow();

returnactualInstance.getSomeProperty();

}

publicvoidsetSomeProperty(StringsomeProperty){

BeanactualInstance=CDI.resolveItSomehow();

actualInstance.setSomeProperty(someProperty);

}

}

Yousee,itextendsthebeanclass,makesitserializable,andusesan“impossibletoclash”classnameandletsallmethodsdelegatetotheactualinstanceobtainedfromtheCDIcontext.You’llprobablyalsoimmediatelyunderstandwhytheCDIbeanmanagementfacilityrequiresthebeanclassestobepublicandhaveapublicdefaultconstructor.You’llalsoseethatwhensuchaproxyclassiscreatedandinjected,theunderlyingactualinstanceisnotnecessarilycreated.It’sonlyautomaticallycreatedwhenthefictiveCDI.resolveItSomehow()methodisinvoked.Underthehood,itwillobtainthecontextfromathreadlocalvariable,exactlyhowFacesContext#getCurrentInstance()works.

Bytheway,EJBalsoworkswithserializableproxiesthisway.That’swhyitcouldseeminglymagicallyperformalltheheavyliftingofstartingorjoiningatransactionandusepooled

Page 427: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

instances.ThelegacyJSF@ManagedBeanfacility,however,didnotuseproxiesatall.That’sexactlywhyitwasimpossibletoinjectaJSFmanagedbeanofanarrowerscopeinaJSFmanagedbeanofabroaderscope.WithCDIbeanmanagementfacilitythisisjustpossible.

NotethatCDIhasalsoa@javax.enterprise.inject.Modelstereotypeannotationwhichbasicallybundlesboth@Namedand@RequestScopedintoasingleannotation.Thisisinnowaydifferentfromarequest-scopedmanagedbean.Unfortunately,itdoesnotrepresentanon-proxyinstance;[email protected]@Modelannotationexistsjustforconvenience.

@DEPENDENTAdependent-scopedmanagedbeanistiedtothelifetimeofthescopewhereinit’sbeingcreatedforthefirsttime.So,ifyouinjectitintoan@ApplicationScoped,thenitwillbecomeapplicationscopedtoo.Andifyouinjectitintoa@ViewScoped,itwillbecomeviewscopedtoo.Andsoon.ThisisthedefaultCDIscope.

Thishas,however,acaveat.WhenyouforgettodeclaretheCDIscopeannotationonyourbackingbean,orimportascopewithexactlythesamenamefromthewrongpackage,e.g.,javax.faces.bean.RequestScopedinsteadofjavax.enterprise.context.RequestScoped,andyoureferenceitdirectlyinEL,asin#{dependentScopedBean},insteadofreferencingitviaanothermanagedbean,asin#{requestScopedBean.dependentScopedBean},

Page 428: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

theneveryELevaluationwillbasicallycreateabrand-newinstancewhichexistsonlywithinthatELcontext.Inotherwords,imagineaJSFformwithtwoinputfieldsandasubmitbutton,eachboundtoadependent-scopedmanagedbean,thenyouwilleffectivelyendupwiththreeseparateinstances.Onewhereinthefirstinputfieldisset,onewhereinthesecondinputfieldisset,andonewhereintheactionmethodisinvoked.So,ifyoueverobserveoddbehaviorofnullsubmittedvaluesintheactionmethodeventhoughtherequiredvalidationhaspassed,thefirstthingtocheckistheactuallyusedCDImanagedbeanscope.

Themajortechnicaldifferencewithotherscopesisthatdependent-scopedmanagedbeansarenotproxied.Inotherwords,what’sbeinginjectedistheactualinstance.

@Dependent

publicclassEntities{

@Produces

publicProductgetProduct(){

returnnewProduct();

}

}

@Named@RequestScoped

publicclassProducts{

@Inject

privateProductproduct;

@Inject

PrivateProductServiceproductService;

publicvoidadd(){

productService.create(product);

}

Page 429: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicProductgetProduct(){

returnproduct;

}

}

Notethatyoustillcan’tuse<h:inputTextvalue="#{product.name}">,becauseitwouldgetitsowninstance.Youstillneedtouse#{products.product.name}.Forexactlythisreason,theproducerisn’[email protected],you’dneedtoforceJSFtorestarttheviewscopebyreturninganon-nulloutcomefromactionmethod;otherwisetheinjectedProductinstancewouldbereusedforthenextview.

Whichscopetochoose?Whichscopetochoosedependssolelyonthedata(instancevariablesakathestate)thebeanholdsandrepresents.Youshouldstrivetoputthestateintheshortestpossibleacceptablescope.Startwitha@RequestScopedbean.Onceyounoticethatsomestateneedstoberetainedafterapostbackonthesameview,splitthatstateexactlyintoanew@ViewScopedbeanwhichyou,inturn,@Injectinthe@RequestScopedbean.OnceyounoticethatsomestateneedstoberetainedonanotherGETrequestwithinthesamesession,splitthatstateexactlyintoanew@ConversationScopedbeanwhichyouinturn@[email protected].

Abusingan@ApplicationScopedbeanforsession-,conversation-,flow-,view-,orrequest-scopeddatawouldmakeittobesharedamongallusers,soanyoneelsecansee

Page 430: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

eachother’sdata,whichisjustplainwrong.Abusinga@SessionScopedbeanforconversation-,flow-,view-,orrequest-scopeddatawouldmakeittobesharedamongallbrowsertabsinthesamesession,sotheendusermayexperienceinconsistencieswheninteractingwitheveryviewafterswitchingbetweentabs,whichisbadforuserexperience.Abusinga@RequestScopedbeanforview-,flow-,orconversation-scopeddatawouldmakeview-,flow-,orconversation-scopeddatabereinitializedtodefaultoneverysingle(Ajax)postback,causingpossiblynon-workingforms.Abusinga@ViewScoped,@FlowScoped,or@ConversationScopedbeanforrequest-,session-,orapplication-scopeddata,andabusinga@SessionScopedbeanforapplication-scopeddatadoesn’taffecttheenduser,butitunnecessarilyoccupiesservermemoryandisplaininefficient.

Notethatthescopeshouldrathernotbechosenbasedonperformanceimplications,unlessyoureallyhavealowmemoryfootprintandwanttogocompletelystateless.You’dthenneedtoexclusivelyusestatelessformswith@RequestScopedbeansandfiddlewithrequestparameterstomaintainanyclient’sstate.Inotherwords,youwouldpossiblyneedtoreinventwhateveralreadyisbeingdonebythejavax.faces.ViewStatehiddeninputfield.

WhereIs@FlashScoped?Atlast,JSFalsosupportstheflashscope.Itisbackedbyashortlivingcookiewhichisassociatedwithadataentryinthesessionscope.Beforetheredirect,acookiewillbesetontheHTTPresponsewithavaluewhichisuniquelyassociatedwith

Page 431: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

thedataentryinthesessionscope.Aftertheredirect,thepresenceoftheflashscopecookiewillbecheckedandthedataentryassociatedwiththecookiewillberemovedfromthesessionscopeandbeputintherequestscopeoftheredirectedrequest.FinallythecookiewillberemovedfromtheHTTPresponse.Thiswaytheredirectedrequesthasaccesstorequest-scopeddatawhichwasbeenpreparedintheinitialrequest.

ThisisactuallynotavailableasamanagedbeanscopebystandardJSFAPI.Inotherwords,thereisnosuchthingas@FlashScoped.TheflashscopeisonlyavailableasamapviaExternalContext#getFlash()inmanagedbeansand#{flash}inEL.Historically,theflashscopewasprimarilyintroducedinordertobeabletoshowafacesmessagesetintheactionmethodintheredirectedpage.Imaginetheusecaseofsavinganeditedproductinadetailpageandredirectingbacktothemasterpage.

publicStringsave(){

FacesContextcontext=FacesContext.getCurrentInstance();

try{

productService.update(product);

context.addMessage(null,newFacesMessage("Product

saved!"));

return"/products?faces-redrect=true";

}

catch(Exceptione){

context.addMessage(null,newFacesMessage(

"Cannotsaveproduct.Error:"+

e.getMessage()));

returnnull;

}

}

Page 432: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Thefacesmessage“Productsaved!”wouldn’tshowupinthe<h:messagesglobalOnly>oftheredirectedpagebecausefacesmessagesareinherentlyrequestscoped(actually,“facescontextscoped”).Historically,duringtheJSF1.xera,thisproblemwassolvedwithaphaselistenerwhichcopiesaftertherenderresponsephaseallundisplayedfacesmessagesintotheHTTPsessionandre-addsaftertherestoreviewphaseanyofthembackintothefacescontext.SincetheintroductionoftheflashscopeinJSF2.0,thisproblemcouldbesolvedinaneasierwaybysimplyinvokingFlash#setKeepMessages() .

productService.update(product);

context.addMessage(null,newFacesMessage("Productsaved!"));

context.getExternalContext().getFlash().setKeepMessages(true)

;

return"/products?faces-redrect=true";

Thisway,thefacesmessagesarebeforeredirectautomaticallystoredintheflashscopeandrestoredafterredirect.

Theflashscopeisnotonlyusefulforfacesmessages.It’salsousefulforpassingentireobjectswhileredirectingfromoneviewtoanotherview,withoutneedingtopasssomeobjectidentifierasarequestparameter.Followingisanexamplewhichpreparesanentityforthenextstepwithoutneedingtosaveitinthedatabasefirst:@Named@RequestScoped//or@ViewScoped

publicclassHome{

privateProductproduct=newProduct();

12

Page 433: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicStringprepareProduct(){

FacesContextcontext=

FacesContext.getCurrentInstance();

context.getExternalContext().getFlash().put("pro

duct",product);

return"/next?faces-redirect=true";

}

publicProductgetProduct(){

returnproduct;

}

}

Wherebythebeanofthenextsteplooksasfollows:

@Named@ViewScoped

publicclassNextimplementsSerializable{

@Inject@ManagedProperty("#{flash.product}")

privateProductproduct;

publicvoidsave(){

//...

}

publicProductgetProduct(){

returnproduct;

}

}

Andthe/next.xhtmlredirectsbackto/home.xhtmlwhentheentityisabsentintheflashscope.

<f:metadata>

<f:viewActionaction="home"if="#{emptyflash.product}">

Page 434: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</f:metadata>

Notethatthisredirectwilltakeplacewhenyouopen/next.xhtmldirectly,orwhenyourefreshthepageinthewebbrowser.Incaseyou’dliketoavoidthat,youcaninstructtheflashscopetokeeptheentryvalueintheflashscopebyprefixingtheentrykeywiththepredefined“keep”keyonthe#{flash}map.

@Named@RequestScoped

publicclassNext{

@Inject@ManagedProperty("#{flash.keep.product}")

privateProductproduct;

//...

}

Thisway,thelifetimeoftheflashscopewillexpanduntiltheenduserclosesthebrowserwindow,orwhentheapplicationnavigatestoadifferentview,orwhentheunderlyingHTTPsessionexpires.Thiswayyoucanevenmakethemanagedbeanrequestscopedinsteadofviewscopedandnotlosetheentitywhilesubmittingaforminthe/next.xhtmlpageorevenrefreshingthepage.Thisisarelativelypowerfulfeatureoftheflashscope.

ManagedbeaninitializationanddestructionManagedbeaninstancescanbeinitializedbasedoninjecteddependenciesina@PostConstructannotatedmethod.Managedbeaninstancescanhookondestroyeventina@PreDestroyannotatedmethod.

Page 435: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Named

publicclassBean{

@PostConstruct

publicvoidinit(){

//...

}

@PreDestroy

publicvoiddestroy(){

//...

}

}

Themethodnamesarenotpredefined.Themethodnamesinit()anddestroy()arebasicallytakenoverfromtheHttpServlet.Youcanofcoursechooseyourown,suchasonload()andcleanup().It’susefultoknowthatthoseannotationsareinheritable.Inotherwords,youcouldputthosemethodsandannotationsinanabstractbaseclass.

Inthepostconstructmethodyouhavetheopportunitytoperforminitializationbasedoninjecteddependencies.Theyarenotavailableintheconstructoryet.Thebeanmanagementfacilitycanonlyinjectthedependenciesafterhavingconstructedthemanagedbeaninstance.Itwillthenimmediatelyinvokethe@PostConstructannotatedmethod.Inthepre-destroymethodyouhavetheopportunitytoperformanynecessarycleanup,suchasclosingresources,deletingfiles,etc.

InjectingJSFvendedtypesBackingbeanscanobviouslybeinjectedasdemonstrated

Page 436: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

manytimesoverintheexamplesabove.Nexttoinjectingyourowntypes,avarietyofJSFvendedtypescanbeinjectedviaCDIaswell.ThesetypeslargelycorrespondtotheimplicitELobjectswesawinTable7-1inChapter7.Thisisnocoincidence.InternallyimplicitobjectsinJSFareimplementedbyso-calledBean<T>instancesfromCDI.TheseCDIBean<T>instancesareeffectivelythefactoryobjectsthatknowhowtogeneratebeans,withwhattypeandoptionalqualifierand/orwithwhatname.

Whenthenameofanimplicitobjectisusedinexpressionlanguage,theCDIELresolverdoesalookupforthatobjectbyname,whichresultsinacalltoacertainBean<T>instance.Whenwe’reinjecting,thetypeintowhichweinject,togetherwithanyexplicitorimplicitqualifiers,formsanalternativekeythat’sbeingusedforthislookup.BothtypesofkeyswillresultintheexactsameCDIBean<T>instancebeingused.

ItshouldbenotedthatcomparedtotheimplicitELobjectsmentionedinTable8-1afewaremissingforCDI-injectableJSFvendedtypes,namely:

#{component}

#{cc}

#{request}

#{session}

#{application}

Both#{component}and#{cc}resolvetoUIComponentwhichisnotinjectable,sincethiswouldrequireaspecialproxyorcustomscopethat’snarrowenoughtoresolvethe“current”instanceofthoseatthetimetheinjectedtypeisaccessed.SincesuchscopeisnotavailableinJSFyet,andtherewasonlyalittletimeandfewresources

Page 437: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

remainingtofinishtheJSF2.3spec,thesehadbeenexcludedfromCDIinjection.

The#{request},#{session},and#{application},respectively,representingHttpServletRequest,HttpSession,andServletContextinaServletcontainerhavebeenomittedsincethesetypesarenotownedbyJSFandthereforeJSFshouldnotprovideCDIinjectioncapabilitiesforthose.ThefactthatJSFdoesprovideimplicitELobjectsforthoseismostlyhistorical.TheonlyspecificationthatshouldprovideinjectionforthosetypesistheServletAPI,whichownsthosetypesdirectly.

Table8-1showstheJSFvendedtypesthatareinjectableviaCDI.

Table8-1 InjectableJSFVendedtypes,AllSince2.3

InjectableJSFtype

Resolvesto

javax.faces.context.FacesContext

FacesContext#getCurrentInstance(  )

javax.faces.context.ExternalContext

FacesContext#getExternalContext(  )

javax.faces.component.UIViewRoot

FacesContext#getViewRoot(  )

javax.faces.context.Flash

ExternalContext#getFlash(  )

@RequestMapMap<String,Object>

ExternalContext#getRequestMap(  )

Page 438: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@RequestMapMap<String,Object>

ExternalContext#getRequestMap(  )

@ViewMapMap<String,Object>

UIViewRoot#getViewMap(  )

@FlowMapMap<Object,Object>

FlowHandler#getCurrentFlowScope(  )

@SessionMapMap<String,Object>

ExternalContext#getSessionMap(  )

@ApplicationMapMap<String,Object>

ExternalContext#getApplicationMap(  )

@InitParameterMapMap<String,String>

ExternalContext#getInitParameterMap(  )

@RequestParameterMapMap<String,String>

ExternalContext#getRequestParameterMap(  )

@RequestParameterValuesMapMap<String,String[]>

ExternalContext#getRequestParameterValuesMap(  )

@HeaderMapMap<String,String>

ExternalContext#getRequestHeaderMap(  )

@HeaderValuesMapMap<String,String[]>

ExternalContext#getRequestHeaderValuesMap(  )

Page 439: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

String[]>

ValuesMap(  )

@RequestCookieMapMap<String,Cookie>

ExternalContext#getRequestCookieMap(  )

javax.faces.application.ResourceHandler

Application#getResourceHandler(  )

AscanbeseenfromTable8-1,forobjectsforwhichthereisonlyoneinstanceandthatinstanceisvended(owned)byJSF,noqualifierisneeded.Whenitconcernsmoregeneraltypes,suchasthevariousmaps,aqualifierisneeded.Allthesequalifierannotationsareavailablefromthejavax.faces.annotationpackage.

Acaveatisthatalloftheabovetypesarerequestscoped,butthetimeduringwhichtheyarevalidisactuallysmaller,namely,fromshortlyafterthemomenttheservice()methodoftheFacesServletiscalleduntilshortlybeforethemomentthatmethodisexited.Careshouldbetakennottoinjectandaccessthesetypesoutsidethattime.It’sexpectedthatafuturerevisionoftheJSFspecwilladdressthisproblem.

ThefollowingshowsanexampleofinjectingtwooftheJSFvendedtypes:

@Named@RequestScoped

publicclassBean{

@Inject

privateFlashflash;

13

Page 440: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Inject@RequestParameterMap

privateMap<String,String>requestParameterMap;

publicvoidsomeMethod(){

if(requestParameterMap.containsKey("something")){

flash.put("aKey","aValue");

}

}

}

EagerInitializationManagedbeansarebydefaultlazilyinitializedwhenevertheyare,forthefirsttime,referencedinanELexpressionorasaninjecteddependency.Managedbeanscanbeeagerlyinitializedonthestartofanyscopebyanobservermethodwhichobservestheinitializationofthescopeofinterest.Oneexamplewaspreviouslygiveninthe“@ViewScoped”section.Themethodpatternisasfollows:publicvoidstartup(@Observes@Initialized(XxxScoped.class)Sscope)

{

//...

}

WhereXxxScoped.classcanbeanyCDI-compatiblescopeandtheSrepresentstheownerofthescope.Forthefollowingscopesthatarethus:

ApplicationScoped.class–javax.servlet.ServletContext

SessionScoped.class–javax.servlet.http.HttpSession

ConversationScoped.class–javax.servlet.ServletRequest

FlowScoped.class–javax.faces.flow.Flow

Page 441: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ViewScoped.class–javax.faces.component.UIViewRoot

RequestScoped.class-javax.servlet.ServletRequest

Notethatthecontainingbeanmustbeofatleastthesamescopeinordertohave@Observes@Initializedtakeeffect.Eagerinitializationhasforanapplication-scopedmanagedbeantheadvantagethatyoucanconfigureitasa“startup”beanwithoutneedingtoreferenceitinsomeotherbeaninordertogetitinitialized.

@ApplicationScoped

publicclassStartup{

publicvoidcontextInitialized

(@Observes@Initialized(ApplicationScoped.class)

ServletContextcontext)

{

//...

}

}

Eagerinitializationhasforarequest-scopedbeantheadvantagethatyoucanifnecessaryinvokeanasynchronousDBquerylongbeforetheFacesServletisinvoked.

@Named@RequestScoped

publicclassEagerProducts{

privateFuture<List<Product>>list;

@Inject

privateProductServiceproductService;

publicvoidrequestInitialized

(@Observes@Initialized(RequestScoped.class)

HttpServletRequestrequest)

Page 442: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

{

if

("/products.xhtml".equals(request.getServletPath())){

list=productService.asyncList();

}

}

publicList<Product>getList(){

try{

returnlist.get();

}

catch(InterruptedExceptione){

Thread.currentThread().interrupt();

thrownewFacesException(e);

}

catch(ExecutionExceptione){

thrownewFacesException(e);

}

}

}

WheretheProductServicelookslikethis:@StatelesspublicclassProductService{

@TransactionAttribute(SUPPORTS)

publicList<Product>list(){

returnentityManager

.createQuery("FROMProductORDERBYid

DESC",Product.class)

.getResultList();

}

@Asynchronous

publicFuture<List<Product>>asyncList(){

returnnewAsyncResult<>(list());

Page 443: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

}

NoteparticularlytherequestInitialized()methodwhichobservesthestartofanyrequestscopeandthusneedstodeterminetheactualpathbeforehandsothatitdoesn’tunnecessarilyhitthebusinessservice.Inthisspecificexample,thatwillonlyhappenoncetherequesthits/products.xhtml.ThatJSFpagecaninturnjustreferencetheproductlistasusual.

<h:dataTablevalue="#{eagerProducts.list}"var="product">

...

</h:dataTable>

WhenopeningthisJSFpage,therequestscopedbeanwillbeinitializedbeforetheFacesServletisinvokedandasynchronouslyfetchtheList<Products>fromthedatabase.Dependingontheserverhardwareused,theavailableserverresourcesandallcoderunningbetweentheinvocationofthefirstservletfilterandenteringtheJSFrenderresponse,thisapproachmaygiveyouatimespaceof10ms~500ms(orperhapsevenmorewhenthere’ssomeinefficientcodeinthepipeline)tofetchdatafromDBinadifferentthreadparallelwiththeHTTPrequest,andthusaspeedimprovementequivalenttothetimetheDBneedstofetchthedata(whichthusdoesn’tneedtobedoneinthesamethreadastheHTTPrequest).

LayersWhileimplementingbackingbeans,it’sveryimportantto

Page 444: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

understandtheimportanceoftheseparationoftheJSFbackingbeanfromtheJPAentityandtheEJBservice.Inotherwords,whendevelopingbackingbeans,youshouldmakesurethatyourbackingbeansareasslickaspossibleandthattheydelegateasmuchaspossiblemodelpropertiestoJPAentitiesandbusinesslogictoEJBservices.YoushouldrealizethatJPAentitiesandEJBservicesshouldbefullyreusableonacompletelydifferentfrontendthanJSF,suchasJAX-RSorevenplainJSP/Servlet.

Thisthusalsomeansthatyoushouldmakesurethatyoudon’tdirectlyorindirectlyincludeaJSF-specificdependencyinaJPAentityoranEJBservice.Forexample,thefollowingapproachisfactuallywrong:@EntitypublicclassProduct{

privateStringname;

privateStringdescription;

publicstaticProductof(ProductBackingbackingBean)

{

Productproduct=newProduct();

product.setName(backingBean.getName());

product.setDescription(backingBean.getDescriptio

n());

returnproduct;

}

//...

}

Here,theJPAentityistightlycoupledtoaJSFbackingbean.

Page 445: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Notonlyaretheentitypropertiesreusedinthebackingbean,butalsotheentityhasadependencyonthebackingbean.Itwouldn’tbepossibletoextracttheentityintoaJARmodulewhichisreusableacrossdifferentJSFwebapplications.

Andthefollowingapproachisalsofactuallywrong:

@Stateless

publicclassProductService{

@Inject

privateEntityManagerentityManager;

publicvoidcreate(Productproduct){

entityManager.persist(product);

FacesContext.getCurrentInstance().addMessage(null,

newFacesMessage("Productcreated!"));

}

}

Here,theEJBpokesthefacescontextandsetsamessagethere.ThiswouldfailwithaNullPointerExceptionwhentheEJBmethodisbeinginvokedfrom,e.g.,aJAX-RSserviceoraServlet,becausethere’snoinstanceofthefacescontextanywhereoverthere.ThisUImessagingtaskisnottheresponsibilityoftheback-endcodebutofthefront-endcode.Inotherwords,addingafacesmessageshouldonlyhappenintheJSFartifact,suchasabackingbean.

Thecorrectapproachisasfollows,asdemonstratedpreviouslyinthesection“Model,View,orController?”:@Named@RequestScoped

publicclassProductBacking{

privateProductproduct=newProduct();

Page 446: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Inject

privateProductServiceproductService;

publicvoidsave(){

productService.create(product);

FacesContext.getCurrentInstance().addMessage(nul

l,

newFacesMessage("Productcreated!"));

}

publicProductgetProduct(){

returnproduct;

}

}

NamingConventionsThereisnostrictconventionspecifiedbyJSFitself.I’veseenthefollowingconventionsacrossvariousJSFprojects:

Foo

FooBean

FooBacking

FooBackingBean

FooManager

FooManagedBean

FooController

wherebyFoocaninturnrepresentoneofthefollowing:JSFviewID,

e.g.,EditProductforeditproduct.xhtml

Page 447: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

JSFviewname,e.g.,Productsforviewproducts.xhtml

JPAentityname,e.g.,Productfor@EntityclassProduct

JSFformID,e.g.,EditProductfor<h:formid="editProduct">

Firstandforemost,namesendingwithBeanlikeFooBean,FooBackingBean,andFooManagedBeanmustbeavoidedtoallextent.The“bean”suffixissuperfluousandtooambiguousaspracticallyanyclassinJavacanbemarkedasaJavaBean.Youdon’timmediatelyuse“ProductBean”foryourJPAentityor“ProductServiceBean”oreven“ProductServiceEnterpriseBean”foryourEJBservice,right?True,#{bean}or#{myBean}oreven#{yourBean}toindicateaJSForCDImanagedbeanisveryoftenusedacrossgenericcodeexamplesinblogs,forums,Q&Asites,andeventhisbook.Butthisismerelydoneforclarityandsimplicityofthecodesnippets.

ThatleavesuswithFoo,FooBacking,FooManager,andFooController.Allareequallyacceptable.Personally,ItendtouseFooBackingforrequest-,view-,flow-,andconversation-scopedbeans,andFooManagerforsession-andapplication-scopedbeans.AstothenamingconventionofFoopart,thatgenerallydependsonwhetherthebackingbeanistightlytiedtoaparticularJSFvieworaJSFform,orgenerallyreusableacrossmultipleJSFviewsorformsreferringaparticularentity.

Inanycase,thisisaprettysubjectiveitemwhichcanhardlybeansweredobjectivelywith“theOneandCorrect”answer.Itreallydoesn’tmatterthatmuchtomeoranyoneelse

Page 448: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

whatyoumakeofit,aslongasyou’reconsistentwithitthroughouttheentireproject.

Footnotes1

https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%9

3controller.

2https://stackoverflow.com/q/3106452/157882.

3https://javaee.github.io/javaee-

spec/javadocs/javax/inject/Named.html.

4http://balusc.omnifaces.org/2013/10/how-to-install-cdi-

in-tomcat.html.

5

http://butunclebob.com/ArticleS.UncleBob.SingletonVsJust

CreateOne.

6

https://github.com/weld/core/blob/master/modules/jsf/src

/main/java/org/jboss/weld/module/jsf/

ConversationAwareViewHandler.java.

7https://javaee.github.io/javaee-

spec/javadocs/javax/faces/flow/builder/FlowBuilder.html.

8https://stackoverflow.com/q/4105439/157882.

9http://showcase.omnifaces.org/cdi/ViewScoped.

10https://developer.mozilla.org/en-

US/docs/Web/API/Navigator/sendBeacon.

11https://en.wikipedia.org/wiki/Law_of_Demeter.

Page 449: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

12https://javaee.github.io/javaee-

spec/javadocs/javax/faces/context/Flash.html#setKeepMess

ages-boolean-.

13https://javaee.github.io/javaee-

spec/javadocs/javax/faces/annotation/package-

summary.html.

Page 450: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1) (2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_9

9.ExceptionHandling

BaukeScholtz andArjanTijms

Willemstad,Curaçao Amsterdam,Noord-Holland,TheNetherlands

Sometimesthingscanunexpectedlygowrong.InJava,thatusuallymanifestsasanExceptionbeingthrown.InJavaEE,it’sbasicallynodifferent.Theissueishowandwhentoproperlyhandlethem.Bydefault,anyuncaughtexceptionduringanHTTPrequestwillendupinadefaulterrorpageprovidedbytheapplicationserver.Figure9-1showswhatWildFly’sdefaultHTTP500errorpagelookslike:

1 2

Page 451: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure9-1 ThedefaultHTTP500errorpageofWildFly11.0.0

TheJSF(JavaServerFaces)implementationbeingusedmayevenprovideitsowndefaulterrorpage.BothMojarraandMyFacesprovideaninternaldefaultimplementationbasedonthejavax.faces.context.ExceptionHandlerAPI, whichisonlyshownwhentheJSFprojectstageissetto1

Page 452: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Development.Figure9-2showshowthatpagelooksforMojarra.

Figure9-2 ThedefaultHTTP500errorpageofMojarra2.3.3indevelopmentstage

ItnotonlyincludesthestacktracebutalsothetextualrepresentationoftheJSFcomponenttreeandanyscopedvariables,whichmightbehelpfulinnailingdowntherootcause,although,inreality,thestacktracealoneandre-executingtheusecasewithadebuggerismuchmorehelpfulthanthat.

CustomErrorPagesWhileusefultotheaveragewebdeveloper,thosedefaulterrorpagesare,quitefrankly,scarytotheaverageenduser.The“look’n’feel”iscompletelyofffromtherestofthesiteandthetextislikeabracadabratotheaverageenduser.Suchanerrorpagedoesn’tevenprovideescapepointsfortheenduser.Thedisgruntledendusercannotquicklyfinditswaytothehomeorcontactpage.Fortunatelyfortheenduser,youcanoverridethosedefaulterrorpagesbyincludinganerrorpage

Page 453: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

inthewebapplicationandregisteringitslocationinan<errorpage>entryinweb.xml.

<errorpage>

<error-code>500</error-code>

<location>WEB-INFerrorpages/500.xhtml</location>

</errorpage>

Thecustomerrorpageispurposefullybeingplacedina/WEB-INFfoldersothatenduserscan’taccessorbookmarkthemdirectly.Bydefault,theservletcontainerwillseterrorpagerelatedattributesintherequestscopewhosekeysaredefinedasjavax.servlet.RequestDispatcher.ERROR_XXX

constants. Thiswayyoucan,ifnecessary,includetheminthecustomerrorpage.Followingisanexample:<dl><dt>RequestURI</dt>

<dd>#

{requestScope['javax.servlet.error.request_uri']}</dd>

<dt>Exceptiontype</dt>

<dd>#{requestScope['javax.servlet.error.exception']

['class']}</dd>

<dt>Exceptionmessage</dt>

<dd>#

{requestScope['javax.servlet.error.exception'].message}

</dd>

<dt>Stacktrace</dt>

<dd><pre>#{

facesContext.externalContext.response.writer.flu

sh()

}#{

2

Page 454: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

requestScope['javax.servlet.error.exception'].pr

intStackTrace

(facesContext.externalContext.response.write

r)

}</pre></dd>

</dl>

Notethetricktoprintthestacktrace.It’simportantthattheresponsewriterisflushedbeforeprintingthestacktrace,andthatthere’snowhitespaceintemplatetextoutsidetheEL(ExpressionLanguage)expressionswithinthe<pre>element,otherwiseitwouldbeappendedtothestacktrace.

Comingbacktothescarinessofsuchanerrorpage,you’dbetterhideawayalltheerrordetailbehindaconditionthatevaluatesonlytruewhentheJSFprojectstageequalsDevelopment.Firstsetanapplication-scopedshortcutvariableinsomemastertemplate:

<c:setvar="DEV"scope="application"

value="#{facesContext.application.projectStageeq

'Development'}">

</c:set>

NowyoucanconditionallydisplaytechnicalinformationintheerrorpagewhentheJSFprojectstageequalsDevelopment.

<c:iftest="#{DEV}">

<h3>Errordetailfordeveloper</h3>

<dl>

...

</dl>

</c:if>

Page 455: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Inanycase,it’sveryimportantthattheerrorpageisentirelystateless.Inotherwords,adecenterrorpagemaynotcontainanyJSFforms,notevenalogoutform.NotonlywillyouavoidtheriskthattheformsubmitfailsbecausetheinitialexceptionwasactuallycausedbyacorruptedJSFstate,butthoseformswillactuallysubmittothewrongURL(uniformresourcelocator),namely,theoneoftheerrorpageitself.Astheerrorpageishiddeninthe/WEB-INFfolder,theformsubmitwouldonlyresultina404error.Insteadofmovingtheerrorpageoutsidethe/WEB-INFfolder,youcouldworkaroundthelogoutcasebyusingaplainHTMLformwhichsubmitstoaplainServlet.TheSecurityContextisalsojustinjectableoverthere,asaresession-scopedmanagedbeans,ifany.

AjaxExceptionHandlingBydefault,whenanexceptionoccursduringaJSFAjaxrequest,theenduserwouldnotgetanyformoffeedbackwhetherornottheactionwassuccessfullyperformed.InMojarra,onlywhentheJSFprojectstageissettoDevelopment,theenduserwouldseeabareJavaScriptalertwithonlytheexceptiontypeandmessage(seeFigure9-3).

Page 456: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure9-3 TheJavaScriptalertwhenanexceptionisthrownduringanAjaxrequestinMojarra2.3.3inDevelopmentstage

Thisisn’tterriblyuseful.AndintheProductionstagetheenduserwouldn’tevengetanyfeedback.Thewebapplicationwouldfailsilently,leavingtheenduserbehindasifnothinghadhappened,whichisjustconfusingandbadforuserexperience.ItwouldmakemoresenseifexceptionsduringJSFAjaxrequestsarehandledthesamewayasexceptionsduringsynchronousrequests,whichreuseexactlythesameerrorpageastheonedeclaredas<errorpage>inweb.xml.Inotherwords,theendusershouldbeabletoseetheerrorpageinfullglory.ThiscanbeachievedbycreatingacustomExceptionHandlerimplementationwhichbasicallyinstructsJSFtocreateanewUIViewRootbasedontheerrorpageandthenbuildandrenderit.It’sonlyquiteabitofcode.Atitssimplestitcanlookasfollows:

publicclassAjaxExceptionHandlerextends

ExceptionHandlerWrapper{

publicAjaxExceptionHandler(ExceptionHandlerwrapped){

super(wrapped);

}

@Override

publicvoidhandle(){

handleAjaxException(FacesContext.getCurrentInstance()

);

getWrapped().handle();

}

protectedvoidhandleAjaxException(FacesContextcontext)

{

Iterator<ExceptionQueuedEvent>

Page 457: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

unhandledExceptionQueuedEvents=

getUnhandledExceptionQueuedEvents().iterator();

if(context==null

||

context.getExternalContext().isResponseCommitted()

||

!context.getPartialViewContext().isAjaxRequest()

||!unhandledExceptionQueuedEvents.hasNext()

){

return;

}

Throwableexception=unhandledExceptionQueuedEvents

.next().getContext().getException();

while(exception.getCause()!=null

&&(exceptioninstanceofFacesException

||exceptioninstanceofELException)

){

exception=exception.getCause();

}

ExternalContextexternal=

context.getExternalContext();

Stringuri=external.getRequestContextPath()

+external.getRequestServletPath();

Map<String,Object>requestScope=

external.getRequestMap();

requestScope.put(RequestDispatcher.ERROR_REQUEST_URI,

uri);

requestScope.put(RequestDispatcher.ERROR_EXCEPTION,

exception);

StringviewId="WEB-INFerrorpages/500.xhtml";

Applicationapplication=context.getApplication();

ViewHandlerviewHandler=

application.getViewHandler();

UIViewRootviewRoot=viewHandler.createView(context,

viewId);

Page 458: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

context.setViewRoot(viewRoot);

try{

external.responseReset();

ViewDeclarationLanguageviewDeclarationLanguage=

viewHandler.getViewDeclarationLanguage(contex

t,viewId);

viewDeclarationLanguage.buildView(context,

viewRoot);

context.getPartialViewContext().setRenderAll(true

);

viewDeclarationLanguage.renderView(context,

viewRoot);

context.responseComplete();

}

catch(IOExceptione){

thrownewFacesException(e);

}

finally{

requestScope.remove(RequestDispatcher.ERROR_EXCEP

TION);

}

unhandledExceptionQueuedEvents.remove();

while(unhandledExceptionQueuedEvents.hasNext()){

unhandledExceptionQueuedEvents.next();

unhandledExceptionQueuedEvents.remove();

}

}

publicstaticclassFactoryextends

ExceptionHandlerFactory{

publicFactory(ExceptionHandlerFactorywrapped){

super(wrapped);

}

@Override

publicExceptionHandlergetExceptionHandler(){

Page 459: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

returnnewAjaxExceptionHandler

(getWrapped().getExceptionHandler());

}

}

}

ThehandleAjaxException()willfirstcheckifthereisafacescontext,iftheresponseisn’tyetcommitted,iftherequestisanAjaxrequest,andifthere’sanyunhandledexceptioneventinthequeue.Ifnoneofthoseconditionsmatches,itwillreturnandletJSFcontinueasusual.

TheHTTPresponseisconsideredcommittedwhentheresponseorapartthereofhasphysicallyalreadybeensenttotheclient.Thisisapointofnoreturn.Youcan’ttakethealreadysentbytesback.Thismayhappenwhentheexceptionoccurshalfwayduringtherenderresponsephase.ThefirsthalfoftheHTTPresponsemayalreadyhavebeensenttotheclient.AlsothedefaultexceptionhandlerofJSFandtheservercan’tdealwithit.Effectively,theclientgetsahalf-bakedHTMLpage.Bestwhatyoucoulddotoavoidthisistomakesurethatyouaren’tperformingbusinesslogicingettermethodsofbackingbeans,whichonitsownisalwaysabadidea,andthatbackingbeansareinitializedassoonaspossible.Thatcouldbeachievedbyexecutingexception-sensitivebusinesslogicin<f:viewAction>insteadof@PostConstruct.AnotheroptionistoincreasetheHTTPresponsebuffersizetomatchthesizeofthegeneratedHTMLresponseofthelargestexception-sensitivepage.Assumingthatit’s100kB,thefollowingweb.xmlcontextparametercanbeused.

<context-param>

Page 460: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<param-name>javax.faces.FACELETS_BUFFER_SIZE</param-name>

<param-value>102400</param-value><!--100kB.-->

</context-param>

ThenextstepinhandleAjaxException()isextractingtherootcauseofinterestfromtheunhandledexceptioneventsqueue.AnyexceptionthatoccursduringtheprocessingoftheJSFlifecyclewillbewrappedinjavax.faces.FacesException.AnyexceptionthatoccursduringtheevaluationofanELexpressionwillbewrappedinjavax.el.ELException.Thosearenotourinterest.

Next,the#{requestScope['javax.servlet.error.request

_uri']}and#{requestScope['javax.servlet.error.excepti

on']}variableswillbesetsothattheerrorpagecanaccessthem.AlsotheUIViewRootinstancerepresentingtheerrorpagewillbecreatedwithhelpoftheViewHandlerandsetintheJSFcontext.Youcould,ifnecessary,conditionallypreparetheviewIDoftheerrorpagebasedontheactualrootcauseoftheexception.Forexample:StringviewId;

if(exceptioninstanceofViewExpiredException){

viewId="WEB-INFerrorpages/expired.xhtml";

}

else{

viewId="WEB-INFerrorpages/500.xhtml";

}

ComingbacktothehandleAjaxException(),inthe

Page 461: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

tryblock,theHTTPresponsebufferwillbecleared,theUIViewRootwillbepopulatedwithcomponents,theAjaxcontextwillbeinstructedtorendertheentireview,theUIViewRootwillberendered,theJSFcontextwillbeinstructedthattheresponseisalreadymanuallytakencareofsothatitwon’tperformanynavigation,andfinallytheexceptionwillberemovedfromtherequestscope.Theremovaloftheexceptioninthefinallyblockisnotmandatory,butservletcontainersexistwhichconsiderthisatriggertowriteaninternalerrorpagetotheresponse,suchasTomcat.

Finally,thequeueofunhandledexceptioneventswillbedrained.Thisisalsonotmandatorybutdoneonpurposesothatitmatchesthedefaultbehaviorofweb.xml-configurederrorpages,andalsotopreventanynextExceptionHandlerinthechainfromhandlinganyremainingexceptionevents.

Inordertogetittorun,youactuallyneedtocreateafactoryaswell.Asmanyotherapplication-widecustomizationsinJSF,customexceptionhandlerscanonlyberegisteredthroughafactory.Itmightlookverbose,butthat’sjustpartofthedesign.Inthisspecificcase,itallowsyoutoreturnadifferentexceptionhandlerimplementationdependingonsomeglobalconfigurationsetting.YoucanfindtheFactoryasanestedclassinthebottomofthepreviouslyshownAjaxExceptionHandlerclass.Itextendsfromjavax.faces.context.ExceptionHandlerFactor

y andcanberegisteredinfaces-config.xmlasfollows:<factory><exceptionhandler-factory>

3

Page 462: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

com.example.project.exceptionhandler.AjaxExcepti

onHandler$Factory

</exceptionhandler-factory>

</factory>

Bytheway,itmustbesaidthatsuchanexceptionhandlerisalsosuitableonnon-Ajaxrequests.YoujusthavetoremovethePartialViewContext#isAjaxRequest()check.YouonlyneedtokeepinmindtomanuallysettheHTTPresponsestatuscodeto500dependingonwhetherornotit’sanAjaxrequest.DothisaftertheExternalContext#responseReset()line.

if(!context.getPartialViewContext().isAjaxRequest()){

external.setResponseStatus

(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

}

WhenyoudosoonanAjaxrequest,theJSFAjaxscriptwon’tprocesstheAjaxresponseasyou’dexpect.Insteadofdisplayingtheerrorpage,itwilltriggertheonerrorhandler.

ViewExpiredExceptionHandlingIfyou’veworkedwithJSFbefore,thenthechanceisgreatthatyou’veseenorheardaboutaViewExpiredException.Tothepoint,itwillbethrownwhentheJSFviewstateassociatedwiththepostbackcannotbefoundintheHTTPsessionanymore—inotherwords,whenthejavax.faces.STATE_SAVING_METHODcontextparameterissettoitsdefaultvalueof“server”,andtheendusersubmitsaJSFformwhoseviewstatecannotbefoundany

Page 463: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

moreontheserverside.Notethatwhenthecontextparameterissetto“client”,youstillneeda“jsf/ClientSideSecretKey”environmententryinordertoavoidexpiredviewswhentheserverrestarts.Thisalliselaboratedinthesection“ViewState”inChapter3.Also,inthesection“@ViewScoped”inChapter8,youcanfindhowtoconfiguretheamountofviewsandmanagedbeansJSFwillsaveintheHTTPsession.

ThereareseveralcircumstanceswhereaViewExpiredExceptionmayunexpectedlyoccur.Allofthemarerelatedtopagenavigationandthebrowsercachewhiletheenduserhasrecentlyloggedoutfromthewebapplication.Normally,theHTTPsessionis,duringlogout,invalidatedasasecuritymeasure,andtheenduserisredirectedtosomelandingpage.Whenthepreviouslyvisitedwebpageiscacheable,andtheenduserpressesthebrowser’sbackbuttonafterlogout,thentheendusermaysuccessfullygettoseethepreviouslyvisitedwebpagefromthebrowsercache.IfitcontainsanyJSFform,thenitsjavax.faces.ViewStatehiddeninputfieldwillactuallyreferaviewstatewhichdoesnotexistinthecurrentsessionanymore.WhentheendusersubmitssuchJSFform,thenJSFwillinevitablythrowaViewExpiredException.

Althoughthisisanexcellentsecuritymeasure,theendusermaygetconfusedasthepreviouslyvisitedwebpageactuallygotsuccessfullyloadedinenduser’sexperience.Thisisnotgoodforuserexperience.You’dthereforebestmakesurethatstatefulJSFpagesarenotcacheable,sothatthewebbrowserisforcedtoactuallyhitthewebserverwhentheenduserpressesthebackbuttonandthusloadafreshnewpage

Page 464: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

withaviewstatewhichisactuallyvalidinthecurrentHTTPsession.ThiscanbeachievedwithaservletfilterwhichsetsspecificresponseheaderswhichinstructtheclientnottocachetheHTTPresponse.Followingisanexampleofsuchaservletfilter:@WebFilter(servletNames="facesServlet")publicclassNoCacheFilterimplementsFilter{

@Override

publicvoiddoFilter

(ServletRequestreq,ServletResponseres,

FilterChainchain)

throwsIOException,ServletException

{

HttpServletRequestrequest=

(HttpServletRequest)req;

HttpServletResponseresponse=

(HttpServletResponse)res;

StringresourcePath=request.getContextPath()

+ResourceHandler.RESOURCE_IDENTIFIER;

if

(!request.getRequestURI().startsWith(resourcePath)){

response.setHeader

("Cache-Control","no-store,must-

revalidate");

}

chain.doFilter(request,response);

}

}

Page 465: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Basically,ithooksonallrequestswhicharegoingtohittheFacesServlet,providedthatit’sconfiguredonaservletnameof“facesServlet”inweb.xmlasfollows:<servlet>

<servlet-name>facesServlet</servlet-name>

<servlet-

class>javax.faces.webapp.FacesServlet</servlet-class>

</servlet>

Alternatively,ifyouhavedividedyourwebapplicationintoapublicareawithonlystatelessJSFpagesandarestrictedareawithonlystatefulJSFpages,thenyoucouldalsomapthisfilteronaspecificURLpatternwhichmatchesonlythestatefulsection,suchasadmin*.

@WebFilter("admin*")

ThefilterexamplechecksfirstiftheHTTPrequestdoesn’trepresentaJSFresourcerequestbeforesettingthecachecontrolheader.Thoserequestsareidentifiedbythe/javax.faces.resourcepathafterthecontextpath,whichisavailablebytheResourceHandler#RESOURCE_IDENTIFIERconstant.ThisisautomaticallyusedwhenyouuseaJSFresourcecomponent,suchas<h:graphicImage>,<h:outputScript>,or<h:outputstyleSheet>.(Seealsothesection“ResourceComponents”inChapter6.)Youdon’twanttodisablethebrowsercacheonthemasthatwouldotherwiseimpactthepageloadingperformance.

ThecachecontrolheaderbeingsetisactuallyonlyrecognizedbyHTTP1.1-capableclients.HTTP1.1was

Page 466: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

introducedin1997.IncaseyouwouldliketocoverHTTP1.0clientsaswell,whichthesedaysaregenerallyonlyancientproxiesorpooruserswhocan’tgetsomethingbetterthanInternetExplorer6.0,thenyou’dbestaddtwomoreresponseheaders.

response.setHeader("Pragma","no-cache");//HTTP1.0.

response.setDateHeader("Expires",0);//Proxies.

Now,withthisfilterinplace,theenduserwon’tanymoregettoseeanyJSFpagefromthebrowsercacheandthuscannolongergetaViewExpiredExceptiononthem.However,therearestillcaseswhereaViewExpiredExceptionisunavoidable.OnesuchcaseisanenduserwhohasseveralJSFpagesopenindifferentbrowsertabsandlogsoutinoneofthemandthenperformsanactioninanothertabwithoutrefreshingthepagebeforehand.Forsuchacase,you’dreallyneeda“Sorry,yoursessionhastimedout”errorpage.Thiscaneasilybeconfiguredasanothererrorpageinweb.xmlasfollows:<errorpage><exception-type>

javax.faces.application.ViewExpiredException

</exception-type>

<location>WEB-INFerrorpages/expired.xhtml</location>

</errorpage>

DonotletitpointtoapublicJSFpage,suchasaloginpage.IncaseyoureallyneedittobeapublicJSFpage,useametarefreshheaderintheerrorpagewhichredirectstheenduserfurthertoapublicJSFpage.

Page 467: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<!DOCTYPEhtml>

<htmllang="en">

<head>

<title>Sessionexpired</title>

<metahttp-equiv="refresh"

content="0;url=#

{request.contextPath}/login.xhtml"/>

</head>

<body>

<h1>Sorry,yoursessionhastimedout</h1>

<h3>Youwillberedirectedtologinpage</h3>

<p>

<ahref="#{request.contextPath}/login.xhtml">

Clickhereiftheredirectdidn'twork,

orwhenyou’reimpatient.

</a>

</p>

</body>

</html>

Notethatthe“0”inthemetarefreshcontentrepresentstheamountofsecondsbeforeredirect.Thus,“0”means“redirectimmediately.”Youcanuse,forexample,“3”toletthebrowserwait3secondsbeforetheredirect.

KeepinmindalsotoconfigurethislocationinanycustomexceptionhandlersuchastheAjaxExceptionHandlerdemonstratedintheprevioussection.Youalsoneedtomakesurethatyour“general”errorpageismappedonanerrorcodeof500insteadofanexceptiontypeof,e.g.,java.lang.Exceptionorjava.lang.Throwable;otherwiseallexceptionswrappedinServletExceptionwouldstillendupinthegeneralerrorpage.JSFwillwrapanyexceptioninaServletExceptionwhenit’sthrownduringasynchronous(non-Ajax)postbackinsteadofan

Page 468: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

asynchronous(Ajax)postback.Theweb.xmlerrorpagemechanismwillonlyextracttherootcausefromtheServletExceptionforasecondpassthrougherrorpagesbyexceptiontypewhennomatchingerrorpagebyexceptiontypeisfound.

AcompletelydifferentalternativetoavoidViewExpiredExceptionistousestatelessJSFviews.ThiswaynothingoftheJSFviewstatewillbesavedandtheJSFviewswillneverexpirebutwilljustberebuiltfromscratchoneveryrequest.Youcanturnonstatelessviewsbysettingthetransientattributeof<f:view>totrue.Thisiselaboratedinthesection“StatelessForms”inChapter4.

Regardlessofthesolution,makesureyoudonotusetheMojarra-specificcom.sun.faces.enableRestoreView11Compatibi

litycontextparameter.ThiswillbasicallyturnonJSF1.0/1.1behaviorwithregardtoexpiredviews.Itwon’tthrowtheViewExpiredExceptionanymore,butneitherdoesitactuallyrestoretheoriginalviewstateatall.Itbasicallyrecreatestheviewandallassociatedviewscopedbeansfromscratchandherebythuslosesalloforiginalstate.Astheapplicationwillbehaveinaconfusingway(“Hey,wherearemyinputvalues..??”),thisisbadforuserexperience.BetterusestatelessJSFviewsinsteadsothatyoucanmanagetheapplicationinspecificviewsonlyinsteadofforallviews.

IOExceptionHandlingSomemethodsoftheunderlyingHttpServletRequestandHttpServletResponseobjectsmaythrowan

Page 469: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

IOException.Usually,thatonlyhappenswhenthenetworkconnectionunexpectedlybreaks:forexample,whentheenduserabruptlystopstheHTTPrequestbypressingtheEscbuttoninthewebbrowser,ortheenduserabruptlynavigatestoadifferentwebpagewhilethecurrentpageisstillloading,orevenwhentheenduser’scomputerornetworkcablecatchesfire.Thosecircumstancesarereallyunavoidable.Foryou,aswebapplicationdeveloper,it’sbesttoletanyIOExceptionbubbleupintotheservletcontainer.Inotherwords,there’sabsolutelynoneedtocatchitasfollows:publicvoidsomeAjaxListener(){

try{

FacesContext.getCurrentInstance()

.getExternalContext().redirect(url);

}

catch(IOExceptione){

thrownewUncheckedIOException(e);

}

}

Instead,justletitgo.

publicvoidsomeAjaxListener()throwsIOException{

FacesContext.getCurrentInstance()

.getExternalContext().redirect(url);

}

EJBExceptionHandlingSometimes,invokingservicemethodsmayalsocauseanexception.Generally,thoseareonpurpose,suchas“entitynot

Page 470: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

found,”“uniqueconstraintviolation,”“invalidusercredentials”,“entityisinmeanwhilemodifiedbyanotheruser,”etc.Bydefault,anynon-application-specificRuntimeException,suchasNullPointerExceptionandevenJPA’sPersistenceExceptionwhichisthrownfromanEJBservicemethod,iswrappedinanEJBException.ThismakesitclumsytonaildowntheactualrootcauseintheJSFactionmethod.

publicvoidaddProduct(){

FacesMessagemessage;

try{

Longid=productService.create(product);

message=new

FacesMessage(FacesMessage.SEVERITY_INFO,

"Productsuccessfullysaved","IDis"+id);

}

catch(EJBExceptione){

if(e.getCause()instanceof

ConstraintViolationException){

message=new

FacesMessage(FacesMessage.SEVERITY_ERROR,

"Duplicateproduct!",e.getMessage());

context.validationFailed();

}

else{

throwe;

}

}

context.addMessage(null,message);

}

Page 471: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Thisisnotthebestpractice.NotonlywouldyouneedtodeterminetherootcauseoftheEJBexceptionbyinspectingException#getCause(),theweb.xmlerrorpagemechanismwouldalsonotbeabletoshowaspecificerrorpagefor,forexample,aConstraintViolationException,becauseit’swrappedinanEJBException.InordertogetEJBtothrowitunwrapped,youneedtocreateacustomexceptionsuperclassfirstwhichyouthenannotatewith@javax.ejb.ApplicationException.

@ApplicationException(rollback=true)

publicabstractclassBusinessExceptionextends

RuntimeException{

publicBusinessException(){

super();

}

publicBusinessException(Exceptioncause){

super(cause);

}

}

Notetherollback=trueattributeontheannotation.Thisisveryimportantincaseyou’dliketheEJBcontainertorollbackanyactivetransactionfromwherethisexceptionisbeingthrown.Followingaresomeexamplesofsubclassesofthiscustombusinessexception.

publicabstractclassQueryExceptionextends

BusinessException{}

publicclassEntityNotFoundExceptionextendsQueryException

{}

publicclassDuplicateEntityExceptionextendsQueryException

{}

4

Page 472: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicabstractclassCredentialsExceptionextends

BusinessException{}

publicclassInvalidUsernameExceptionextends

CredentialsException{}

publicclassInvalidPasswordExceptionextends

CredentialsException{}

Notethatyoudon’tnecessarilyneedtorepeatthe@ApplicationExceptionoverallsubclassesasit’salready@Inherited.Followingaresomeconcreteusecasesonwhichthoseexceptionscouldbethrown:publicUsergetById(Longid){

try{

returnentityManager

.createQuery("FROMUseruWHEREu.id=:id",

User.class)

.setParameter("id",id)

.getSingleResult();

}

catch(NoResultExceptione){

thrownewEntityNotFoundException(e);

}

}

publicOptional<User>findByEmail(Stringemail){

try{

returnOptional.of(entityManager

.createQuery("FROMUseru"

+"WHEREu.email=:email",User.class)

.setParameter("email",email)

.getSingleResult());

}

Page 473: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

catch(NoResultExceptione){

returnOptional.empty();

}

}

publicUsergetByEmailAndPassword(Stringemail,String

password){

Useruser=findByEmail(email)

.orElseThrow(InvalidUsernameException::new);

Credentialscredentials=user.getCredentials();

byte[]passwordHash=digest(password,

credentials.getSalt());

if(!Arrays.equals(passwordHash,

credentials.getPasswordHash())){

thrownewInvalidPasswordException();

}

returnuser;

}

publicLongcreate(Useruser){

if(findByEmail(user.getEmail()).isPresent()){

thrownewDuplicateEntityException();

}

entityManager.persist(user);

returnuser.getId();

}

IntheJSFbackingbeanactionmethods,youcouldthenhandlethemaccordingly.

Page 474: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicvoidsignup(){

FacesMessagemessage;

try{

userService.create(product);

message=newFacesMessage("Youaresuccessfully

signedup!");

}

catch(DuplicateEntityExceptione){

message=new

FacesMessage(FacesMessage.SEVERITY_ERROR,

"Sorry,usernamealreadytaken!",

e.getMessage());

context.validationFailed();

}

context.addMessage(null,message);

}

Inordertofurtherreducetheboilerplatecode,youcouldevenletallbusinessexceptionsgoandhaveacustomexceptionhandlertohandlethem.

publicclassBusinessExceptionHandlerextends

ExceptionHandlerWrapper{

publicBusinessExceptionHandler(ExceptionHandlerwrapped)

{

super(wrapped);

}

@Override

publicvoidhandle(){

handleBusinessException(FacesContext.getCurrentInstan

ce());

getWrapped().handle();

}

Page 475: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

protectedvoidhandleBusinessException(FacesContext

context){

Iterator<ExceptionQueuedEvent>

unhandledExceptionQueuedEvents=

getUnhandledExceptionQueuedEvents().iterator();

if(context==null

||!unhandledExceptionQueuedEvents.hasNext()

){

return;

}

Throwableexception=unhandledExceptionQueuedEvents

.next().getContext().getException();

while(exception.getCause()!=null

&&(exceptioninstanceofFacesException

||exceptioninstanceofELException)

){

exception=exception.getCause();

}

if(!(exceptioninstanceofBusinessException)){

return;

}

context.addMessage(null,newFacesMessage(

FacesMessage.SEVERITY_FATAL,

exception.toString(),

exception.getMessage()));

context.validationFailed();

context.getPartialViewContext()

.getRenderIds().add("globalMessages");

unhandledExceptionQueuedEvents.remove();

while(unhandledExceptionQueuedEvents.hasNext()){

unhandledExceptionQueuedEvents.next();

unhandledExceptionQueuedEvents.remove();

}

Page 476: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

publicstaticclassFactoryextends

ExceptionHandlerFactory{

publicFactory(ExceptionHandlerFactorywrapped){

super(wrapped);

}

@Override

publicExceptionHandlergetExceptionHandler(){

returnnewBusinessExceptionHandler

(getWrapped().getExceptionHandler());

}

}

}

Yes,it’sindeedsimilartotheAjaxExceptionHandlerasshownintheearliersection“AjaxExceptionHandler.”However,thefirstdifferenceisthatitdoesn’tskiphandlingtheexceptionwhentheresponseisalreadycommittedorwhenit’snotanAjaxrequest.Theseconddifferenceisthelogicbetweenextractingtherootcauseoftheexceptionanddrainingtheremainingunhandledexceptionevents.ThisBusinessExceptionHandlerwillinsteadcheckiftherootcauseisaninstanceofBusinessExceptionandifso,itwilladdafacesmessageandinstructJSFtoexplicitlyupdatethecomponentidentifiedby“globalMessages”,whichshouldrefertoaglobalmessagescomponentinyourmastertemplatesomethinglikethefollowing:<h:messagesid="globalMessages"globalOnly="true"/>

Ultimately,allbusinessexception-relatedfacesmessageswillendupthere.Youmighthavenoticedthatthefacescontextis

Page 477: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

explicitlymarkedas“validationfailed”viatheFacesContext#validationFailed()call.ThisisgenerallyusefulincaseanycodeintheFacelettemplateisrelyingonit.IfyouwouldliketorunittogetherwiththeAjaxExceptionHandlerfornon-businessexceptions,thenyouneedtoregisteritinthefaces-config.xmlaftertheAjaxExceptionHandler.Anythingthatisdeclaredlaterinthefaces-config.xmlwilleffectivelywrapthepreviouslydeclaredone.Thisalsoappliestocustomexceptionhandlerfactories.WhentheBusinessExceptionHandlerconfirmsthattheexceptionisnotaninstanceofBusinessException,thenitwillleavetheunhandledexceptioninthequeueandreturnfromthemethodandfinallydelegatetothehandle()methodofthewrappedExceptionHandler,whichshallbetheAjaxExceptionHandler.

<factory>

<exceptionhandler-factory>

com.example.project.AjaxExceptionHandler$Factory

</exceptionhandler-factory>

<exceptionhandler-factory>

com.example.project.BusinessExceptionHandler$Factory

</exceptionhandler-factory>

</factory>

WiththeBusinessExceptionHandlerinplace,youcouldfurtherreducethebackingbeanactionmethodasfollows:publicvoidsignup(){userService.create(product);

context.addMessage(null,

newFacesMessage("Youaresuccessfullysigned

Page 478: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

up!");

}

Footnotes1https://javaee.github.io/javaee-

spec/javadocs/javax/faces/context/ExceptionHandler.html.

2https://javaee.github.io/javaee-

spec/javadocs/javax/servlet/RequestDispatcher.html#ERROR

_EXCEPTION.

3https://javaee.github.io/javaee-

spec/javadocs/javax/faces/context/ExceptionHandlerFactor

y.html.

4https://javaee.github.io/javaee-

spec/javadocs/javax/ejb/ApplicationException.html.

Page 479: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1) (2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_10

10.WebSocketPush

BaukeScholtz andArjanTijms

Willemstad,Curaçao Amsterdam,Noord-Holland,TheNetherlands

JSF1.0introducedHTMLform-basedPOSTactionsupport.JSF2.0introducedAJAX-basedPOSTsupport.JSF2.0introducedGETquerystringparametermappingsupport.JSF2.2introducedGET-basedactionsupport.JSF2.3introducesWebSocketsupport.

JSF’sWebSocketsupportisrepresentedbythenew<f:websocket>tag,thePushContextinterface,andthe@Pushannotation.ItisbuiltontopoftheJSR-356WebSocketsspecification,introducedinJavaEE7.Therefore,itistechnicallypossibletouseitinaJavaEE7environmentaswell.JSR-356isevennativelysupportedinTomcatsince7.0.27andinJettysince9.1.0.

InMojarra,the<f:websocket>hasanadditionalJavaEE7dependency:JSON-P(JSR-353).Incaseyou’retargetingTomcatorJettyinsteadofaJavaEEapplicationserver,youmightneedtoinstallitseparately.JSON-PisinternallyusedtoconvertJavaobjectstoaJSONstringsothatitcan,withoutmuchhassle,betransferredtotheclientsideandbeprovided

1 2

Page 480: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

asanargumentofJavaScriptlistenerfunctionattachedto<f:websocket>.

ConfigurationTheJSR-356WebSocketspecificationdoesnotofficiallysupportprogrammaticinitializationofthesocketendpointduringruntime.Sowecannotinitializeitbysimplydeclaring<f:websocket>intheviewandwaituntilaJSFpagereferencingitisbeingopenedforthefirsttime.Wereallyneedtoinitializeitexplicitlyduringdeploymenttime.Wecoulddothatbydefault,buthavinganunusedWebSocketendpointopenforeverisnotreallyniceifit’sneverusedbythewebapplication.Sowecannotavoidhavingacontextparametertoexplicitlyinitializeitduringdeploymenttime.

<context-param>

<param-name>javax.faces.ENABLE_WEBSOCKET_ENDPOINT</param-

name>

<param-value>true</param-value>

</context-param>

Ifyoupreferprogrammaticinitializationoverdeclarativeinitialization,thenyoucanalwaysuseServletContext#setInitParameter()inaServletContainerInitializerofyourwebfragmentlibraryasfollows:publicclassYourInitializerimplementsServletContainerInitializer{

@Override

publicvoidonStartup(Set<Class<?>>types,

ServletContextcontext){

Page 481: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

context.setInitParameter(

PushContext.ENABLE_WEBSOCKET_ENDPOINT_PARAM_

NAME,"true");

}

}

NotethatitisnotpossibletoperformthistaskinaServletContextListenerasJSFwillactuallyregistertheWebSocketendpointinitsownServletContainerInitializerimplementationwhichalwaysrunsbeforeanyServletContextListener.

OncetheWebSocketendpointisenabledandsuccessfullyinitializedduringdeployment,itwilllistenforWebSockethandshakerequestsontheURL(uniformresourcelocator)patternjavax.faces.push*.ThefirstpathelementwillrepresenttheWebSocketchannelname.

Comingbackto“officially,”someWebSocketimplementationsdo,however,supportprogrammaticinitialization,suchastheoneprovidedbyUndertow,whichisinturnusedinWildFly.Unfortunately,thespecdoesn’tsayso,andtheremaybeWebSocketimplementationsthatsimplydonotsupportprogrammaticinitialization,suchasTyrusasusedinPayara.

TheWebSocketcontainerwill,bydefault,listenforhandshakerequestsonthesameportastheapplicationserverislisteningforHTTPrequests.Youcanoptionallychangetheportwithanotherweb.xmlcontextparameter,<context-param>

<param-

name>javax.faces.WEBSOCKET_ENDPOINT_PORT</param-name>

1

Page 482: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<param-value>8000</param-value>

</context-param>

orprogrammaticallyinaServletContainerInitializer:context.setInitParameter(

PushContext.WEBSOCKET_ENDPOINT_PORT_PARAM_NAME,

"8000");

UsageInyourJSFpage,justdeclarethe<f:websocket>tagalongwiththerequiredchannelattributerepresentingthechannelnameandtheoptionalonmessageattributerepresentingareferencetoaJavaScriptfunction.

<f:websocketchannel="test"onmessage="logMessage"/>

<script>

functionlogMessage(message,channel,event){

console.log(message);

}

</script>

TheJavaScriptfunctionwillbeinvokedwiththreearguments.1. message:thepushmessageasJSONobject.

2. channel:theWebSocketchannelname.Thismaybeusefulincaseyouintendtohaveagloballistener,orwanttomanuallycontrolthecloseoftheWebSocket.

3. event:theoriginalMessageEventobject.ThismaybeusefulincaseyouintendtoinspectitintheJavaScriptfunction.

OntheWARside,youcaninjectthePushContextviathe

Page 483: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@PushannotationinanywebartifactthatsupportsCDIinjection.ThiscanbeasimpleCDImanagedbean,butitcanalsobea@WebServlet,@WebFilteror@WebListener.

importjavax.inject.Named;

importjavax.enterprise.context.RequestScoped;

importjavax.inject.Inject;

importjavax.faces.push.Push;

importjavax.faces.push.PushContext;

@Named@RequestScoped

publicclassBean{

@Inject@Push

privatePushContexttest;

publicvoidsubmit(){

test.send("HelloWorld!");

}

}

ThePushContextvariablenametestmustmatchthechannelnamedeclaredintheJSFpage.Incaseyoucannotmatchthevariablenamewiththechannelname,youcanalwaysspecifythechannelnameintheoptionalchannelattributeofthe@Pushannotation.

@Inject@Push(channel="test")

privatePushContextfoo;

Oncethesubmit()methodofthebeanshownbeforeisinvokedbysomeJSFcommandcomponent,eveninadifferentJSFpage,thepushmessage“HelloWorld!”willbe

Page 484: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

senttoallopenedsocketsontheverysamechannelname,applicationwide.

ScopesandUsersAsyoumayhaverealized,<f:websocket>isthus,bydefault,applicationscoped.Youcancontrolthescopebytheoptionalscopeattribute.Allowedvaluesareapplication,session,andview.

Whensettosession,themessagewillbesenttoallopenedsocketsontheverysamechannelinthecurrentsessiononly.

<f:websocketchannel="progress"scope="session"/>

Thisisparticularlyusefulforprogressmessagescomingfromlong-runningsession-scopedbackgroundtasksinitiatedbytheuseritself.Thiswaytheusercanjustcontinuebrowsingthesitewithouttheneedtowaitfortheresultontheverysamepage.

Alternatively,youcanalsosettheoptionaluserattributetoaserializablevaluerepresentingtheuniqueuseridentifier,whichcanbeaStringrepresentingtheuserloginnameoraLongrepresentingtheuserID.Whenthisattributeisset,thescopeofthesocketwillautomaticallydefaulttosessionanditcannotbesettoapplication.

<f:websocketchannel="chat"user="#{loggedInUser.id}"/>

Thisofferstheopportunitytosendamessagetoaspecificuserasfollows:privateStringmessage;

Page 485: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

privateUserrecipient;

@Inject@Push

privatePushContextchat;

publicvoidsendMessage(){

LongrecipientId=recipient.getId();

chat.send(message,recipientId);

}

YoucanevensendittomultipleusersbyprovidingaSetargument.

privateStringmessage;

privateSet<User>recipients;

@Inject@Push

privatePushContextchat;

publicvoidsendMessage(){

Set<Long>recipientIds=recipients.stream()

.map(User::getId)

.collect(Collectors.toSet());

chat.send(message,recipientIds);

}

Inotherwords,youcaneasilyimplementachatboxthisway.Incidentally,real-timeusertargetednotificationsat,forexample,StackOverflowandFacebookworkthisway.

Whenthescopeissettoview,themessagewillbesenttotheopenedsocketonthespecifiedchannelinthecurrentviewonly.Thiswon’taffectanysocketsonthesamechannelinallotherviewsthroughouttheapplication.

Page 486: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:websocketchannel="push"scope="view"/>

Thisisalsosupportedincombinationwiththeuserattribute.

<f:websocketchannel="chat"user="#{user.id}"scope="view"/>

Thisconstructissomewhatunusualthoughandshouldonlybeusedifthelogged-inuserrepresentedbyuserattributecanhaveashorterlifetimethantheHTTPsession.Thisis,however,inturnconsideredapoorsecuritypractice.Thebestsecuritypracticeis,namely,thattheHTTPsessionisinvalidatedduringloginandduringlogout.InvalidatingtheHTTPsessionduringloginpreventssessionfixationattacksandinvalidatingthesessionduringlogoutpreventsdirtyuser-specificdatalingeringaroundinHTTPsession.

ChannelDesignHintsYoucandeclaremultiplepushchannelsondifferentscopeswithorwithoutusertargetthroughouttheapplication.However,beawarethatthesamechannelnamecaneasilybereusedacrossmultipleviews,evenifitisviewscoped.It’smoreefficientifyouuseasfewdifferentchannelnamesaspossibleandtiethechannelnametoaspecificpushsocketscope/usercombination,nottoaspecificJSFview.Incaseyouintendtohavemultipleview-scopedchannelsfordifferentpurposes,itisbesttouseonlyoneview-scopedchannelandhaveaglobalJavaScriptlistenerwhichcandistinguishitstaskbasedonthedeliveredmessage,forexample,bysendingthemessageinaserverasfollows,Map<String,Object>message

Page 487: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

=newHashMap<>();

message.put("functionName","someFunction");

message.put("functionData",functionData);//CanbeMap

orBean.

someChannel.send(message);

whichisthenprocessedintheonmessageJavaScriptlistenerfunctionasfollows:functionsomeSocketListener(message){

window[message.functionName](message.functionData);

}

functionsomeFunction(data){

//...

}

functionotherFunction(data){

//...

}

//...

One-TimePushYoucanusetheconnectedattributetopreventthesocketfromautomaticallyconnectingduringpageload.

<f:websocket...connected="false"/>

Thisisparticularlyusefulwhenyouwanttoperformaone-timepushoftheresultafterinvokingaview-scopedAjax

Page 488: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

actionmethodwhichmighttakeabitmoretimetocomplete,andyou’dliketheusertoimmediatelycontinueusingtheverysamepagewithoutbeingannoyedabouta“slowwebsite”experience.Thisapproachonlyrequiresabitofadditionalworkwiththejsf.pushJavaScriptAPI(applicationprogramminginterface). Ithasthreefunctions,butonlytwoareofinteresttous:jsf.push.open(...)andjsf.push.close(...).Thethirdone,jsf.push.init(...),basicallyinitializesthesocketandthat’suptotherendererofthe<f:websocket>tag.

RightbeforeinvokingtheAjaxactionmethod,you’dneedtoexplicitlyopenthesocketbyinvokingthejsf.push.open(...)functionwiththesocketclientIDasargument.Andrightafterthepushmessagearrives,you’dneedtoexplicitlyclosethesocketbyinvokingthejsf.push.close(...)functionwiththesocketclientIDasargument.Thefollowingexampledemonstratesthisapproach:<script>functionstartLongRunningProcess(){

jsf.push.open("push");

document.getElementById("status").innerHTML=

"Longrunningprocesshasstarted...";

}

functionendLongRunningProcess(result){

jsf.push.close("push");

document.getElementById("status").innerHTML=

result;

}

</script>

<h:form>

2

Page 489: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:commandButtonvalue="submit"

onclick="startLongRunningProcess()"

action="#{longRunningProcess.submit}">

<f:ajax/>

</h:commandButton>

</h:form>

<divid="status"/>

<f:websocketid="push"channel="push"scope="view"

connected="false"onmessage="endLongRunningProcess">

</f:websocket>

Itmustbesaidthatit’sapoorpracticetoputJavaScriptcoderightintheHTMLsourceasshownabove.It’s,ofcourse,fordemonstrationpurposesonly.Forbettermaintenance,performance,andtoolingsupport,youshould,inreal-worldcode,putJavaScriptcodeinaJSfileandincludeitvia<h:outputScript>.AndthenI’mnottalkingaboutthelackofjQuerymagicfordemonstrationpurposes.

Intheexample,openingthesocketisperformedduringtheonclickofthecommandbutton.Theonmessagelistenerfunctioninturnclosesthesocket.Ofcourse,youcanalsokeepthesocketopenallthetimewithoutfiddlingwithJavaScript,butitmaybeawasteofresourcesifthesocketisn’tusedforpurposesotherthanpresentingtheresultofaview-scopedAjaxactionmethod.Hereiswhattheassociatedbackingbeanlookslike.

@Named@RequestScoped

publicclassLongRunningProcess{

@Inject

privateLongRunningProcessServiceservice;

Page 490: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Inject@Push

privatePushContextpush;

publicvoidsubmit(){

service.asyncSubmit(result->push.send(result));

}

}

Andhereiswhattheserviceclasslookslike.

@Stateless

publicclassLongRunningProcessService{

@Asynchronous

publicvoidasyncSubmit(Consumer<String>callback){

Stringresult=someLongRunningProcess();

callback.accept(result);

}

}

NotetheEJB@Asynchronousannotation.Thisisveryimportantinthisconstruct.ItwillensurethattheEJB(EnterpriseJavaBean)methodisexecutedinaseparatethread.ThisallowsthebackingbeanmethodtoreturnimmediatelywithoutwaitingfortheEJBmethodtocomplete.

StatefulUIUpdatesAsyoumayhavenoticed,theonmessageJavaScriptlistenerfunctionisgenerallyonlyusefulforsmallstatelesstasks,suchasdisplayingafeedbackmessageoraddinganewitemtosomestatelesslistusingJavaScript.Itisn’tterriblyusefulwhenyouwanttoupdateastatefulUI(userinterface)

Page 491: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

representedbyanotherJSFcomponent.ThinkofreplacingatrivialloadingimagewithawholeJSFtable.

Forthatyou’dbetternest<f:ajax>listeningonaspecificpushmessage.ViaitsrenderattributeyouhavetheopportunitytoautomaticallyupdateanarbitraryJSFcomponentinanincomingpushmessage.Followingisanexamplewhichinitiallyshowsaloadingimageandthenthetablewhenit’sreadytoload:

<h:form>

<f:websocketchannel="push"scope="view">

<f:ajaxevent="loaded"render=":results"/>

</f:websocket>

</h:form>

<h:panelGroupid="results"layout="block">

<h:graphicImagename="images/loading.gif"

rendered="#{emptylongRunningSearch.results}">

</h:graphicImage>

<h:dataTablevalue="#{longRunningSearch.results}"

var="result"

rendered="#{notemptylongRunningSearch.results}">

<h:column>#{result.id}</h:column>

<h:column>#{result.name}</h:column>

<h:column>#{result.value}</h:column>

</h:dataTable>

</h:panelGroup>

Notethat<f:websocket>isplacedin<h:form>.Thisismandatorywhenithas<f:ajax>nested.Normallythisisnotrequired.Hereiswhatthebackingbeanlookslike.

@Named@ViewScoped

publicclassLongRunningSearchimplementsSerializable{

privateList<Result>results;

Page 492: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Inject

privateLongRunningSearchServiceservice;

@Inject@Push

privatePushContextpush;

@PostConstruct

publicvoidinit(){

service.asyncLoadResults(results->{

this.results=results;

push.send("loaded");

});

}

publicList<Result>getResults(){

returnresults;

}

}

Notethatthepushmessage"loaded"matchesexactlythe<f:ajaxevent>value.Youcanuseanyvalueyouwantandyoucannestasmany<f:ajax>tagsasyouneed.It’simportantthatthemanagedbeanis@ViewScopedastheAjaxcallisbasicallyperformedinadifferentrequestwithinthesameview.Finallytheserviceclasslooksasfollows:@Stateless

publicclassLongRunningSearchService{

@Asynchronous

publicvoidasyncLoadResults(Consumer<List<Result>>

callback){

List<Result>results=someLongRunningProcess();

callback.accept(results);

}

}

Page 493: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ThesomeLongRunningProcess()methodrepresentsyourimplementationofsomelong-runningprocess(e.g.,callingathird-partywebserviceAPI).

Site-WidePushNotificationsForthis,youcanuseanapplication-scopedsocket.Suchasocketisparticularlyusefulforapplication-widefeedbackmessagestriggeredbythewebapplicationitselfonaparticulareventwhichmaybeinteresttoallapplicationusers.Thinkofsite-widestatistics,real-timelists,stockupdates,etc.Thefollowingexampleshowsthecaseofareal-timetop10list:

<h:dataTableid="top10"value="#{bean.top10}"var="item">

<h:column>#{item.ranking}</h:column>

<h:column>#{item.name}</h:column>

<h:column>#{item.score}</h:column>

</h:dataTable>

<h:form>

<f:websocketchannel="top10Observer">

<f:ajaxevent="updated"render=":top10"/>

</f:websocket>

</h:form>

Hereiswhattheserviceclasslookslike,withalittlehelpfromCDIevents.

@Stateless

publicclassItemService{

@Inject

privateEntityManagerentityManager;

@Inject

privateBeanManagerbeanManager;

Page 494: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicvoidupdate(Itemitem){

List<Item>previousTop10=getTop10();

entityManager.merge(item);

List<Item>currentTop10=getTop10();

if(!currentTop10.equals(previousTop10)){

beanManager.fireEvent(newTop10UpdatedEvent());

}

}

pulicList<Item>getTop10(){

returnentityManager

.createNamedQuery("Item.top10",Item.class)

.getResultList();

}

}

NotethattheTop10UpdatedEventis,inthisspecificexample,basicallyjustanemptyclasslikepublicclassTop10UpdatedEvent{}.Alsonotethatwe’renotinjectingthePushContexthere.Thisisotherwiseconsideredtightcouplingoflayers.AllJSF-relatedcodebelongsinthefrontend,notinthebackend.Thiswaytheback-endserviceclassesarebetterreusableacrossallkindsoffront-endframeworksotherthanJSF,suchasJAX-RSorevenplainvanillaJSP/Servlet.Inotherwords,youshouldensurethatnoneofyourback-endclassesdirectlyorindirectlyuseanyfront-end-specificclassessuchasthosefromjavax.faces.*,javax.ws.*,andjavax.servlet.*packages.

AnyeventfiredwiththeBeanManager#fireEvent()methodcanbeobservedusingCDI@Observesannotation.Thisworksacrossall

Page 495: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

layers.Inotherwords,evenwhenit’sfiredinthebackend,youcanobserveitinthefrontend.Theonlyrequirementisthatthebackingbeanmustbe@ApplicationScoped.Thatis,there’snotnecessarilyanymeansofanHTTPrequest,HTTPsession,orJSFviewanywhereatthatmoment.

@Named@ApplicationScoped

publicclassBean{

privateList<Item>top10;

@Inject

privateItemServiceservice;

@Inject@Push

privatePushContexttop10Observer;

@PostConstruct

publicvoidload(){

top10=service.getTop10();

}

publicvoidonTop10Updated(@ObservesTop10UpdatedEvent

event){

load();

top10Observer.send("updated");

}

publicList<Item>getTop10(){

returntop10;

}

}

KeepingTrackofActiveSocketsInordertokeeptrackofactivesockets,youcaninan

Page 496: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

[email protected]@WebsocketEvent.Closedevents.Thefollowingexampleassumesthatyouhave<f:websocketchannel="chat"user="...">andthatyouintendtocollect“activechatusers”:@ApplicationScopedpublicclassWebsocketEventObserver{

privateMap<Serializable,AtomicInteger>users;

@PostConstruct

publicvoidinit(){

users=newConcurrentHashMap<>();

}

publicvoidonopen(@Observes@OpenedWebsocketEvent

event){

if("chat".equals(event.getChannel())){

getCounter(event.getUser()).incrementAndGet(

);

}

}

publicvoidonclose(@Observes@ClosedWebsocketEvent

event){

if("chat".equals(event.getChannel())){

getCounter(event.getUser()).decrementAndGet(

);

}

}

Page 497: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

privateAtomicIntegergetCounter(Serializableuser)

{

returnusers.computeIfAbsent(user,k->new

AtomicInteger());

}

publicSet<Serializable>getActiveUsers(){

returnusers.entrySet().stream()

.filter(entry->entry.getValue().intValue()

>0)

.map(entry->entry.getKey())

.collect(Collectors.toSet());

}

}

YoucanusetheabovegetActiveUsers()methodtoobtainasetof“activechatusers.”Donotethatasingleusercanopenthesamewebpagemultipletimeswithinthesamesession(e.g.,multiplebrowsertabs)andthat’sexactlywhyacounterisusedinsteadsimplyaddingandremovingusersfromaSet.

DetectingSessionandViewExpirationThe<f:websocket>tagwillbydefaultkeeptheconnectionopenforever,aslongasthedocumentisopen—aslongasthere’snoconnected="false"beingset,orjsf.push.close(clientId)beinginvoked,ofcourse.Whenthefirstconnectionattemptfails,itwillimmediatelyreportanerror.Youcanoptionallyusetheoncloseattribute

Page 498: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

toreferenceaJavaScriptfunctionwhichactsasacloselistener.

<f:websocket...onclose="logClose"/>

<script>

functionlogClose(code,channel,event){

if(code==-1){

//WebSocketAPInotsupportedbyclient.E.g.

IE9.

}

elseif(code==1000){

//Normalcloseasresultofexpiredviewor

session.

}

else{

//Abnormalcloseasresultofaclientorserver

error.

}

}

</script>

TheJavaScriptfunctionwillbeinvokedwiththreearguments.1. code:theclosereasoncodeasinteger.Ifthisis-1,thentheWebSocket

JavaScriptAPIissimplynotsupported bytheclient.Ifthisis1000,thenanormalclosurehasoccurredasconsequenceofanexpiredvieworsessionintheserverside.

2. channel:theWebSocketchannelname.Thismaybeusefulincaseyouintendtohaveagloballistener.

3. event:theoriginalCloseEventobject.ThismaybeusefulincaseyouintendtoinspectitintheJavaScriptfunction.

Whenthefirstconnectionattemptsucceedsbutitlatergetsdisconnectedforsomereason(e.g.,becausetheserverisrestarting),thenitwillbydefaultkeeptryingtoreconnect.InthecaseofMojarra,itwillkeepretryingupto25times,with

3

Page 499: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

anintervalwhichisincremented500mseachtime,anditwilleventuallyreportanerror.

Asyoumighthavenoticedintheaforementionedoncloselistenerfunctionexample,youcouldjustcheckiftheclosecodeofa<f:websocket>equals1000inordertoperformsomeclient-sideactionviaJavaScript(e.g.,displayingawarningmessageand/orredirectingtosome“Sessionexpired”page).

<f:websocketchannel="push"scope="session"

onclose="closeListener"/>

<script>

functioncloseListener(code){

if(code==1000){

window.location=jsf.contextPath+

"/expired.xhtml";

}

}

</script>

Thisworksforbothview-andsession-scopedsockets.Application-scopedsockets,however,remainopenforeveraslongasthedocumentisstillopenonclientside,evenwhentheunderlyingvieworsessionhasexpired.

BreakingDownMojarra’sf:websocketImplementationThe<f:websocket>APIspecifiesthefollowingclassesandmethods:

javax.faces.push.Push,aCDIqualifiertofor@Inject.Withhelpofthisqualifierthesocketchannelnamecanbespecified.

Page 500: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

javax.faces.push.PushContext,aninterfacewiththreesend()methods:send(Stringmessage),send(Stringmessage,Suser),andsend(Stringmessage,Collection<S>users).AllthosemethodsacceptthepushmessageasObjectandwillforJavaScriptconvertittoaJSONstring.AllthosemethodsreturnFuture<Void>foreachmessage.Ifitreturnsnull,thenthetargetsocketisn’topenatall.Ifitdoesn’tthrowExecutionExceptiononFuture#get()methodcall,thenthemessagewassuccessfullydelivered.

javax.faces.component.UIWebsocket,acomponentwhichimplementsClientBehaviorHolderinordertosupportnested<f:ajax>.Historically,theprototypetagusedaTagHandlerinsteadofUIComponent.Itwaslaterdecidedtoletthetagsupport<f:ajax>asthatwouldmakecomplexandstatefulUIupdatesmucheasier.However,itisn’tpossibletoletaTagHandlerimplementClientBehaviorHolderandbenefitallofbuilt-inAjaxmagic,hencetheconversiontoUIComponent.

ViewHandler#getWebsocketURL()methodwhichtakesachannelnameandreturnstheabsoluteWebSocketURLinformofws://host:port/contextjavax.faces.pushchannelwithhelpofExternalContext#encodeWebsocketURL().

ExternalContext#encodeWebsocketURL()methodwhichbasicallytakesarelativeWebSocketURIinformof/contextjavax.faces.pushchannelandreturnstheabsoluteWebSocketURL.

Theactualimplementationisfairlyextensive.It’sdirectlybasedonOmniFaces<o:socket> with,hereandthere,afewadjustmentssuchasusingacomponent’sclientIDinsteadofachannelnameinJavaScriptAPIfunctions.

com.sun.faces.renderkit.html_basic.WebsocketRenderer,afacesrendererclasswhichregistersduringencodingthesocketchannel,scopeanduserinWebsocketChannelManagerandretrievestheWebSocketURLfromit.Thenitauto-includesthejsf.jsscriptcontainingthenecessaryjavax.push.*functions,andrendersthejsf.push.init(...)inlinescriptfunctioncallwithamongotherstheWebSocketURLasanargument.ThisfunctionshouldinturninJavaScriptcreateanewWebSocket(url).TheWebsocketRendererwillalso

4

Page 501: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

subscribetheWebsocketFacesListenertothecurrentview.

com.sun.faces.push.WebsocketChannelManager,asession-scopedCDImanagedbeanwhichkeepstrackofallsofarregistered<f:websocket>channels,scopes,andusersandensuresthateachsocketgetsitsownuniquechannelidentifier.ItwillregistereverychannelidentifierinWebsocketSessionManagerandthoseofuser-targetedsocketsinWebsocketUserManager.

com.sun.faces.push.WebsocketFacesListener,asystemeventlistenerwhichlistensonPreRenderViewEventandrendersifnecessarythejsf.push.open(...)orjsf.push.close(...)inlinescriptfunctioncallsdependingonwhethertheconnectedattributerepresentsadynamicELexpressionwhichgotchangedduringanAjaxrequest.

com.sun.faces.push.WebsocketEndpoint,aclasswhichimplementsJSR-356javax.websocket.EndpointandlistensontheURItemplatejavax.faces.push{channel}.WhenanewWebSocket(url)iscreatedandopenedonclient-sideJavaScript,thenanewjavax.websocket.Sessioniscreatedonserver-sideJavaandtheWebsocketEndpointwilladdthisSessiontoWebsocketSessionManager.Equivalently,whenasocketisclosed,thentheWebsocketEndpointwillremoveitfromWebsocketSessionManager.

com.sun.faces.push.WebsocketSessionManager,anapplication-scopedCDImanagedbeanwhichcollectsallsofaropenedsocketsessionsandvalidatesthattheiruniqueWebSocketURLhasbeenregisteredbyWebsocketChannelManager.

com.sun.faces.push.WebsocketUserManager,anapplication-scopedCDImanagedbeanwhichcollectsthechannelidentifiersofallsofaropeneduser-targetedsockets.

com.sun.faces.push.WebsocketPushContext,theconcreteimplementationofthePushContextinterface.ItwillsendthepushmessageviaWebsocketSessionManagerandifnecessaryobtaintheuser-targetedchannelsviaWebsocketUserManager.

com.sun.faces.push.WebsocketPushContextProducer,theCDIproducerwhichcreatestheWebsocketPushContextinstancebasedonchannelnameasobtainedfrom@Pushqualifier,theWebsocketSessionManagerandWebsocketUserManager.

Page 502: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Footnotes1https://github.com/javaee/websocket-spec/issues/211.

2

https://javaserverfaces.github.io/docs/2.3/jsdocs/symbol

s/jsf.push.html.

3http://caniuse.com/#feat=websockets.

4http://showcase.omnifaces.org/push/socket.

Page 503: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1) (2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_11

11.CustomComponents

BaukeScholtz andArjanTijms

Willemstad,Curaçao Amsterdam,Noord-Holland,TheNetherlands

InChapter7youshouldhavelearnedthatFacelettemplatesasin<ui:composition>,<ui:include>,and<ui:decorate>areusefulwhenyouwanttosplitmainpagelayoutfragmentsintoreusabletemplates,suchasheader,menu,maincontent,andfooter.AndthatFacelettagfilessuchas<t:field>areusefulwhenyouwanttohaveareusablegroupofcomponentsinordertominimizecodeduplication.Andthatcompositecomponentssuchas<t:inputLocalTime>areusefulwhenyouwanttocreateacustomcomponentwithasingleresponsibilitybasedonexistingcomponentsand,ifnecessary,abunchofHTML.

However,theremaybecasesinwhichnosinglecomponentexistsforthepurposeyouhadinmind,evennotwhencomposedofexistingcomponentsandabunchofHTML.Or,perhapsthecomponentdoesexist,butitsrendererisnotdoingthingsyouhadinmind.Atthispoint,youwouldneedtocreateacustomcomponentoracustomrenderer.

JSF(JavaServerFaces)hassincethebeginningoffereda

1 2

Page 504: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

highdegreeofabstractionaroundtheUIComponentAPI(applicationprogramminginterface).Youcancustomizecomponentsbycreatingabrand-newcustomUIComponent,orbyextendinganexistingcomponentfromthestandardHTMLcomponentset,orbypluggingacustomRendererforanexistingcomponent.

ComponentType,Family,andRendererTypeEachUIComponentinstancehas“componenttype,”“componentfamily,”and“renderertype”associatedwithit.Thecomponenttypebasicallyrepresentstheuniquecomponentidentifierassociatedwiththecomponenttag.ItcanberegisteredintoJSFviaeitherthe@FacesComponentannotationorthe<component>entryinfaces-config.xml.Thefollowingexampledemonstratestheusageoftheannotationonaminimalcomponentclass:@FacesComponent(SomeComponent.COMPONENT_TYPE)

publicclassSomeComponentextendsUIComponentBase{

publicstaticfinalStringCOMPONENT_TYPE=

"project.SomeComponent";

publicstaticfinalStringCOMPONENT_FAMILY=

"project.SomeFamily";

publicSomeComponent(){

setRendererType(SomeRenderer.RENDERER_TYPE);

}

Page 505: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Override

publicStringgetFamily(){

returnCOMPONENT_FAMILY;

}

}

Andthefollowingexampledemonstratestheusagewiththeentryinfaces-config.xml:<component><component-type>project.SomeComponent</component-

type>

<component-class>

com.example.project.component.SomeComponent

</component-class>

</component>

NotethatwhenyouregisteraJavaEEartifactviaboththeannotationandtheXMLwaysusingthesameidentifier,thentheXMLdeclarationwillalwaysgetprecedenceovertheannotationdeclaration.ThesameholdstrueforallJSFannotations.

ThepublicconstantsCOMPONENT_TYPEandCOMPONENT_FAMILYinthecomponentclassarenotmandatory,buttheyfollowthesameconventionasthestandardJSFcomponentsetdoesandthereforegivemoreconsistencyindevelopingwithJSF.ThepublicconstantCOMPONENT_TYPEallowsthedevelopertoprogrammaticallycreatethecomponentwithoutneedingtohard-codethecomponenttype.

UIComponentcomponent=FacesContext.getCurrentInstance()

.getApplication().createComponent(SomeComponent.COMPONENT

_TYPE);

Page 506: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

NotethatprogrammaticallycreatingUIComponentinstancesthiswayisgenerallynotthenormalpracticeinanaverageJSFwebapplication.Instead,younormallydefinethecomponentsintheviewandleavethejobofcreatingUIComponentinstancesuptoJSForanypluggablecomponentlibrary.InthecaseofFaceletsviewtechnology,thecomponenttagscanberegisteredintoJSFviaeither@FacesComponent(createTag=true)ora<tag>entryina*.taglib.xmlfilealongwiththecomponenttypeasfollows:<tag><tag-name>someComponent</tag-name>

<component>

<component-

type>project.SomeComponent</component-type>

</component>

</tag>

Assaid,thestandardJSFcomponentsethasthecomponenttypealsodefinedintheconcreteUIComponentclassesbehindthecomponenttags.ThoseUIComponentclassesarealllocatedinthejavax.faces.component.htmlpackage.TheUIComponentclassnamecanbederivedfromthecomponenttagnamebyprefixingitwith“Html”.Soisthecomponenttag<h:outputText>backedbytheHtmlOutputTextcomponent.JSFcan,viaeitherthe@FacesComponentannotationorthe<component>entryinfaces-config.xml,figureoutwhichcomponentclassexactlyisassociatedwiththecomponenttag,soJSFknowsthat,forthe<h:outputText>tag,itshouldcreateaconcreteHtmlOutputTextinstance.

Page 507: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

OnceJSFhastheconcreteUIComponentinstanceathand,itcanfigureoutthecomponentfamilyaswellastherenderertypebyinvokingthemethodsUIComponent#getFamily()andUIComponent#getRendererType(),respectively.ThisinformationismandatoryinordertocreateaconcreteRendererinstanceforthegivenUIComponentinstance,asyoucanseeinthefollowingsnippet:Rendererrenderer=FacesContext.getCurrentInstance().getRenderKit()

.getRenderer(component.getFamily(),

component.getRendererType());

Thecomponentfamilyisbasicallya“hard-coded”constantwhichcanbesharedacrossmultiplecomponenttypes.It’s“hard-coded”insuchwaythatthere’snosetterforit.ThisisneededinordertogettheconcreteRendererinstancesastheyarenotregisteredintoJSFbycomponenttypebutratherbycomponentfamily.Thisallowsthedeveloperofthepluggablerenderkittoregistertherenderertypejustonceinsteadofmultipletimesforeachknownstandardcomponenttypeandunknowncustomcomponenttype.Normally,thecomponentfamilyandrenderertypeareregisteredintoJSFviaeitherthe@FacesRendererannotationorthe<renderer>entryinfaces-config.xml.Thefollowingexampledemonstratestheusagewiththeannotationonaminimalrendererclass.

@FacesRenderer(

componentFamily=SomeComponent.COMPONENT_FAMILY,

rendererType=SomeRenderer.RENDERER_TYPE)

publicclassSomeRendererextendsRenderer{

publicstaticfinalStringRENDERER_TYPE=

Page 508: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

"project.SomeRenderer";

}

Andthefollowingexampledemonstratestheusagewiththeentryinfaces-config.xml:<renderkit><renderer>

<component-family>project.SomeFamily</component-

family>

<renderer-type>project.SomeRenderer</renderer-

type>

<renderer-class>

com.example.project.renderer.SomeRenderer

</renderer-class>

</renderer>

</renderkit>

Therenderertypeisbydefaultdefinedintheconstructoroftheconcretecomponentclass,asyoumightalreadyhavenoticedinthecodesnippetoftheSomeComponentclassasshownpreviously.Incaseit’sneeded,thecomponentsubclassdeveloperorevenyourselfascomponentendusercanalwaysoverridethedefaultrendererinstanceofacomponentwiththedesiredrendererinstance.Thiscanbedoneinvariousways,allviaXML.Thefirstwayisviathe<tag>entryassociatedwiththecomponenttaginthe*.taglib.xmlfile.

<tag>

<tag-name>someComponent</tag-name>

<component>

<component-type>project.SomeComponent</component-

type>

<renderer-type>custom.OtherRenderer</renderer-type>

</component>

Page 509: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</tag>

Thisaffectsapplication-wideandtargetsonlythespecificcomponenttag.Thesecondwayisviaanew<renderer>entryinfaces-config.xmlwhichtargetsexactlythedesiredcomponentfamilyanditsdefaultrenderertype.

<renderkit>

<renderer>

<component-family>project.SomeFamily</component-

family>

<renderer-type>project.SomeRenderer</renderer-type>

<renderer-class>

com.example.custom.renderers.OtherRenderer

</renderer-class>

</renderer>

</renderkit>

Thisaffectsapplication-wideandtargetseverycomponenttagassociatedwiththegivencomponentfamilycurrentlyassociatedwiththegivenrenderertype.ThethirdwayisviatherendererTypeattributeofthecomponenttag.

<x:someComponent...rendererType="custom.OtherRenderer"/>

Thisaffectsonlythedeclaredcomponenttagandnotothers.Table11-1providesanoverviewofallcomponenttypes,families,andrenderertypesofthestandardJSFcomponentset.

Table11-1 Componentclass,componenttype,componentfamilyandrenderertypeofstandardJSFHTMLcomponentset

Page 510: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Componenttag

Componentclass

Componenttype

Componentfamily

Renderertype

<h:body>

HtmlBody

javax.faces.OutputBody

javax.faces.Output

javax.faces.Body

<h:button>

HtmlOutcomeTargetButton

javax.faces.HtmlOutcomeTargetButton

javax.faces.OutcomeTarget

javax.faces.Button

<h:column>

HtmlColumn

javax.faces.HtmlColumn

javax.faces.Column

null

<h:commandButton>

HtmlCommandButton

javax.faces.HtmlCommandButton

javax.faces.Command

javax.faces.Button

<h:commandLink>

HtmlCommandLink

javax.faces.HtmlCommandLink

javax.faces.Command

javax.faces.Link

<h:commandScript>

HtmlCommandScript

javax.faces.HtmlCommandScript

javax.faces.Command

javax.faces.Script

<h:dataTable>

HtmlDataTable

javax.faces.HtmlDataTable

javax.faces.Data

javax.faces.Table

<h:doctype

HtmlDocty

javax.faces.Outp

javax.faces

javax.faces.

Page 511: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:doctype>

HtmlDoctype

javax.faces.OutputDoctype

javax.faces.Output

javax.faces.Doctype

<h:form>

HtmlForm

javax.faces.HtmlForm

javax.faces.Form

javax.faces.Form

<h:graphicImage>

HtmlGraphicImage

javax.faces.HtmlGraphicImage

javax.faces.Graphic

javax.faces.Image

<h:head>

HtmlHead

javax.faces.OutputHead

javax.faces.Output

javax.faces.Head

<h:inputFile>

HtmlInputFile

javax.faces.HtmlInputFile

javax.faces.Input

javax.faces.File

<h:inputHidden>

HtmlInputHidden

javax.faces.HtmlInputHidden

javax.faces.Input

javax.faces.Hidden

<h:inputSecret>

HtmlInputSecret

javax.faces.HtmlInputSecret

javax.faces.Input

javax.faces.Secret

<h:inputText>

HtmlInputText

javax.faces.HtmlInputText

javax.faces.Input

javax.faces.Text

<h:inputTextarea>

HtmlInputTextarea

javax.faces.HtmlInputTextarea

javax.faces.Input

javax.faces.Textarea

Page 512: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:link>

HtmlOutcomeTargetLink

javax.faces.HtmlOutcomeTargetLink

javax.faces.OutcomeTarget

javax.faces.Link

<h:message>

HtmlMessage

javax.faces.HtmlMessage

javax.faces.Message

javax.faces.Message

<h:messages>

HtmlMessages

javax.faces.HtmlMessages

javax.faces.Messages

javax.faces.Messages

<h:outputFormat>

HtmlOutputFormat

javax.faces.HtmlOutputFormat

javax.faces.Output

javax.faces.Format

<h:outputLabel>

HtmlOutputLabel

javax.faces.HtmlOutputLabel

javax.faces.Output

javax.faces.Label

<h:outputText>

HtmlOutputText

javax.faces.HtmlOutputText

javax.faces.Output

javax.faces.Text

<h:outputScript>

UIOutput

javax.faces.Output

javax.faces.Output

javax.faces.Script

<h:outputStylesheet>

UIOutput

javax.faces.Output

javax.faces.Output

javax.faces.resource.Stylesheet

Page 513: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:panelGrid>

HtmlPanelGrid

javax.faces.HtmlPanelGrid

javax.faces.Panel

javax.faces.Grid

<h:panelGr

oup>

HtmlPanel

Group

javax.faces.Html

PanelGroup

javax.faces

.Panel

javax.faces.

Group

<h:selectBooleanCheckbox>

HtmlSelectBooleanCheckbox

javax.faces.HtmlSelectBooleanCheckbox

javax.faces.SelectBoolean

javax.faces.Checkbox

<h:selectManyCheckbox>

HtmlSelectManyCheckbox

javax.faces.HtmlSelectManyCheckbox

javax.faces.SelectMany

javax.faces.Checkbox

<h:selectManyListbox>

HtmlSelectManyListbox

javax.faces.HtmlSelectManyListbox

javax.faces.SelectMany

javax.faces.Listbox

<h:selectManyMenu>

HtmlSelectManyMenu

javax.faces.HtmlSelectManyMenu

javax.faces.SelectMany

javax.faces.Menu

<h:selectOneListbox>

HtmlSelectOneListbox

javax.faces.HtmlSelectOneListbox

javax.faces.SelectOne

javax.faces.Listbox

<h:selectOneMenu>

HtmlSelectOneMenu

javax.faces.HtmlSelectOneMenu

javax.faces.SelectOne

javax.faces.Menu

Page 514: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:selectOneRadio>

HtmlSelectOneRadio

javax.faces.HtmlSelectOneRadio

javax.faces.SelectOne

javax.faces.Radio

Ifyoucarefullyinspectthetable,you’llseeacertainpatterninthecomponentfamilyandrenderertype,particularlywithinput,select,andcommandcomponents.You’llnoticethatarenderertypecanbesharedacrossmultiplecomponents,evenofadifferentfamily.

You’llalsonoticethatthere’soneHTMLcomponentwithoutarenderertype,<h:column>.Thisisaspecialcomponentwhichcannotbeusedstand-alonebutcanonlybeusedwhennestedinaspecificparentcomponent.FromthestandardJSFcomponentset,that’ssofaronly<h:dataTable>.ItsrendererrecognizeschildrenofthetypeUIColumnandcanactonthemaccordingly.

CreatingNewComponentandRendererIfyoupaidcloserattentiontoTable3-1inChapter3,youmighthavenoticedthatJSFdoesn’tprovideanycomponenttorenderadynamic<ul>or<ol>oreven<dl>elementbasedonaprovidedarrayorcollectionvalue.Itonlysupportsthatforthe<table>element.True,thesamecouldbeachievedwithFacelets<ui:repeat>andabitofcustomHTMLcode,butwe’lltakethisasanopportunitytocreateanewcustomcomponentwhichrendersan<ul>or<ol>.

ThefirststepistocheckwhichUIComponentsubclassissuitableforthetaskwehaveinmind,sothatwecanreducethecustomcodelogictoaminimum.Inthe

Page 515: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

javax.faces.componentpackageyoucanfindabunchofUIXxxcomponentsubclasses.Ifyouwanttocreateanewformcomponent,extendfromUIForm.Ifyouwanttocreateanewinputcomponent,extendfromUIInput.Ifyouwanttocreateanewoutputcomponent,extendfromUIOutput.Ifyouwanttocreateanewdataiteratorcomponent,extendfromUIData.ThereisrarelyanyneedtoextendfromUIComponentdirectly.We’dliketobeabletoiterateoveracollectioninordertogenerate<li>elementsinsidethe<ul>,sowe’llpickUIData.Ithasalotofiterationandstatesavinglogicalreadyimplemented.Followingisthecustomcomponentclasscom.example.project.component.DataList:@FacesComponent(createTag=true)

publicclassDataListextendsUIData{

publicDataList(){

setRendererType(DataListRenderer.RENDERER_TYPE);

}

}

Isthatreallyall?Yes,theUIDatasuperclassalreadyhsdeverythingweneedandalltheHTMLproducingcodejustgoesintotheDataListRendererwhichwillbeshownshortly.You’llnoticethatthe@FacesComponentannotationdeclaresacreateTag=trueattribute.ThisbasicallyinstructsJSFthatitshouldautomaticallycreateacomponenttagthepredefinedXMLnamespacehttp://xmlns.jcp.org/jsf/component.Inotherwords,theabovetagisavailableintheFaceletsfileas

Page 516: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

follows:<...xmlns:my="http://xmlns.jcp.org/jsf/component">

...

<my:dataList...>

...

</my:dataList>

TheXMLnamespaceprefix“my”isofcourseyourchoice.Generally,you’dliketopicksomesortofabbreviationofyourcompanynamehere.YoucanalsooverridethepredefinedXMLnamespacewiththenamespaceattribute.

@FacesComponent(createTag=true,

namespace="http://example.com/ui")

Thiswillthenbeavailableasfollows:

<...xmlns:ex="http://example.com/ui">

...

<ex:dataList...>

...

</ex:dataList>

Thisnamespaceisunfortunatelynotunifiablewiththe<namespace>ofacustom*.taglib.xmlfile.Ifyouusethesamenamespaceforboth,thenJSFwillpreferthe*.taglib.xmloneoverthe@FacesComponentoneandhencebeunabletofindthecustomcomponenttag.Thatis,inpracticallyanythingJavaEErelated,anyXML-basedregistrationofathinghashigherprecedencethanJavaannotation-basedregistrationoftheverysamething.

You’dbasicallyneedtoexplicitlyregisterthecustom

Page 517: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

componentoverthereinthe*.taglib.xmlfileaswell.Here’showtheWEB-INFexample.taglib.xmlascreatedinthesection“TagFiles”inChapter7couldbeextendedwiththeregistrationofthecustomcomponent,whichisessentiallythesameaswhatthe@FacesComponentisdoingforyou.

<?xmlversion="1.0"encoding="UTF-8"?>

<facelettaglib

xmlns:="http://xmlns.jcp.org/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee

http://xmlns.jcp.org/xml/ns/javaee/web-

facelettaglibrary_2_3.xsd"

version="2.3"

>

<namespace>http://example.com/tags</namespace>

<short-name>t</short-name>

<!--Othertagshere-->

<tag>

<description>RendersaHTMLlist.</description>

<tag-name>dataList</tag-name>

<component>

<component-type>dataList</component-type>

</component>

</tag>

</facelettaglib>

Thiswaythecustomcomponentisavailableinthesamenamespaceastheothertags.

<...xmlns:t="http://example.com/tags">

...

<t:dataList...>

...

Page 518: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</t:dataList>

Now,youcanessentiallyremoveallattributesofthe@[email protected],asyouhaveseenintheexample.taglib.xml,thecomponenttypedefaultstotheclassnamewiththefirstcharacterlowercased.Youcanalwaysoverrideitbyexplicitlyspecifyingitasthevalueofthe@FacesComponentannotation.Generally,you’dliketoprefixitwiththenameofthecompany.It’sagoodpracticetodefineitasapublicconstantsothatotherscould,ifnecessary,lookitupintheJavadocand/oruseitforApplication#createComponent().

@FacesComponent(DataList.COMPONENT_TYPE)

publicclassDataListextendsUIData{

publicstaticfinalStringCOMPONENT_TYPE=

"example.DataList";

publicDataList(){

setRendererType(DataListRenderer.RENDERER_TYPE);

}

}

Nowadjustthecomponenttypeintheexample.taglib.xmlaccordingly.

<component>

<component-type>example.DataList</component-type>

</component>

The*.taglib.xmlalsogivesyouroomtoregisterthe

Page 519: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

attributesvia<attribute>entries,althoughthatmayendupinsomeverbosecode.Youshouldalreadyhaveseenthatinthesection“Tagfiles”inChapter7.Unfortunately,thecurrentJSFversiondoesn’tofferanannotationtodeclarativelydeclarean“official”componentattribute.There’snosuchthingas@FacesAttributeprivateIterablevalue.Yet.ThismaycomeinaJSF.next.Thenon-officialway,withoutany<attribute>,alsoworksjustfine.Youcandeclareanyattributeyouwantonthecomponenttagintheview.

<t:dataListfoo="bar"bar="foo"/>

That’sjustthefreedomofXML.Whethertheactualcomponentorrendererimplementationactuallydoessomethingwithitisanotherstory.YoucouldevendeclareacustomattributeonanexistingcomponentandjustpluganextendedRendererinordertoprocessthatattribute.Morelaterinthesection“ExtendingExistingRenderer.”Talkingaboutrenderers,our<t:dataList>stillneedsitsrendererasregisteredinitsconstructor.Here’swhatthecom.example.project.renderer.DataListRende

rerlookslike.

@FacesRenderer(

componentFamily=UIData.COMPONENT_FAMILY,

rendererType=DataListRenderer.RENDERER_TYPE)

publicclassDataListRendererextendsRenderer{

publicstaticfinalStringRENDERER_TYPE=

"example.List";

@Override

Page 520: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicvoidencodeBegin

(FacesContextcontext,UIComponentcomponent)

throwsIOException

{

ResponseWriterwriter=context.getResponseWriter();

UIDatadata=(UIData)component;

if(data.getRowCount()>0){

writer.startElement("ul",component);

}

}

@Override

publicvoidencodeChildren

(FacesContextcontext,UIComponentcomponent)

throwsIOException

{

ResponseWriterwriter=context.getResponseWriter();

UIDatadata=(UIData)component;

for(inti=0;i<data.getRowCount();i++){

data.setRowIndex(i);

writer.startElement("li",component);

if(component.getChildCount()>0){

for(UIComponentchild:

component.getChildren()){

child.encodeAll(context);

}

}

writer.endElement("li");

}

data.setRowIndex(-1);

}

@Override

publicvoidencodeEnd

(FacesContextcontext,UIComponentcomponent)

Page 521: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

throwsIOException

{

ResponseWriterwriter=context.getResponseWriter();

UIDatadata=(UIData)component;

if(data.getRowCount()>0){

writer.endElement("ul");

}

}

}

Inhindsight,it’srelativelysimple.We’redelegatingasmuchaspossibleofthehardworktotheJSF-providedUIDatasuperclass.IntheencodeBegin()youstartthe<ul>elementwhenthedatamodelisnotempty.ThisistobecheckedbyexaminingtheresultofUIData#getRowCount().ItsJavadoc basicallysays:

Returnthenumberofrowsintheunderlyingdatamodel.Ifthenumberofavailablerowsisunknown,return-1.

Theterm“rows”isindeedstronglyrelatedtotables.Thisisalsowhatthissuperclassisoriginallydesignedfor:<h:dataTable>.Theterm“item”wouldhavebeenmoregeneral,butitiswhatitis.

Then,intheencodeChildren()method,wesetthecurrentrowindexviatheUIData#setRowIndex()method,startthe<li>element,encodeallchildrenasisviaUIComponent#encodeAll()oneachofthem,andfinallyendthe<li>element.Oncetheloopisdone,weexplicitlymakeitcleartotheUIDatasuperclassbyinvokingUIData#setRowIndex()withavalueof-1.ItsJavadocsays:

1

2

Page 522: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

IfthenewrowIndexvalueis-1:Ifthevarpropertyisnotnull,removethecorrespondingrequestscopeattribute(ifany).Resetthestateinformationforalldescendantcomponents.

Itthusclearsoutanystaterelatedtotheiteration.Thisisveryimportant;zotherwiseitmightcausesideeffectsfurtherdowninthecomponenttreeorevencauseacorruptedviewstatewhenitbyitselfneedstotraversethedatamodel.Finally,intheencodeEnd()method,itwillendthe<ul>elementbasedonsameconditionsasinencodeBegin().

TheUIData#setRowIndex()callintheencodeChildren()methodwillunderthehoodextractthedatamodelfromthevalueattributeandwrapitinasuitableimplementationofthejavax.faces.model.DataModelabstractclass. Sofar,aspertheJavadocofUIData#getValue(), thefollowingtypesoftheobjectbehindthevalueattributearesupported,inthisscanningorder:

1. java.util.List(since1.0)

2. Arrays(since1.0)

3. java.sql.ResultSet(since1.0)

4. javax.servlet.jsp.jstl.sql.Result(since1.0)

5. java.util.Collection(since2.2)

6. java.lang.Iterable(since2.3)

7. java.util.Map(since2.3)

8. TypesforwhichasuitableDataModelhasbeenregisteredvia@FacesDataModel(since2.3)

9. AllothertypeswillbeadaptedusingtheScalarDataModelclass,whichwilltreattheobjectasasinglerowofdata(since1.0)

Youwouldindeednotexpecttoseeanyonepassingarounda

3

4

Page 523: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

plainjava.sql.ResultSetinamodernJavaEEapplication,letaloneseeJSPpageswithJSTL<sql:xxx>tags.Butthisallisforbackwardcompatibility.Remember,JSFwasintroducedin2004.Backwardcompatibilitywasoneofitsstrongestkeysinsurvivinguptotoday.They’recertainlycandidatestoberemoved,butnotnow.

And,there’sindeedanoverlapbetweensometypes;ListandCollectioncouldeasilybecoveredbyIterableastheybothimplementthisinterface.Butthishasaperformancereason.ForaList,theitemsareaccesseddirectlybyindexbytheListDataModel;foraCollection,theitemsareextractedfirstviaCollection#toArray()andthenaccessedbyindexbytheCollectionDataModel;andforanIterable,theitemsaresimplyiteratedandcollectedintoanewListfirstbytheIterableDataModel.Itmaymakeadifference.

You’llalsoseethatJSF2.3hasnotonlyaddedtwonewdatamodelsbutevenintroducesanewannotationtoregisteracustomone.Previously,you’dneedtomanuallywrapthecustomcollectioninthecustomdatamodeleverytimebeforepassingtoaUIDatacomponent.TheDataModelabstractclasshasthedisadvantageofnotbeingSerializableitselfwhichmoreorlessforcesyoutomakea@ViewScopedbeanholdingsuchadatamodeltohavealazyloadinggetteronatransientdatamodelpropertyasfollows:privateYourCollectionyourCollection;

privatetransientYourDataModeldataModel;

publicDataModelgetDataModel(){

if(dataModel==null){

Page 524: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

dataModel=newYourDataModel(yourCollection);

}

returndataModel;

}

Ideally,theUIDatashouldbyitselfrecognizeYourCollectiontypeandautomaticallywrapitinaYourDataModel.Thenew@FacesDataModelannotationdoesexactlythat.

@FacesDataModel(forClass=YourCollection.class)

publicclassYourDataModel<E>extendsDataModel<E>{}

Comingbacktothecustomrenderer,there’sonemethodleftunexplained:thegetRendersChildren().It’sbeenoverriddentoexplicitlyreturntrue.You’llprobablyaskyourself,whywasitinitiallyfalse?WhynotjustletitbethedefaultbehaviorofencodeChildren()andrelyonanyoverriddenencodeChildren()methodwhetheritwantstoinvokeencodeAll()onthechildren?Thiswasactuallyanhistoricoversightinthespecification.Originally,theencodeAll()methoddidn’texist.ItwasonlyaddedinJSF1.2anditbasicallymadethegetRendersChildren()obsolete.Butforbackwardcompatibilitythiscomplexitywasintroduced.

Inanutshell,alwaysletgetRendersChildren()returntrueifyouhaveoverriddentheencodeChildren()method.Otherwisethechildrenwon’tbeencodedatall.

Lastbutnotleast,you’llprobablyalsowonderwhywedon’t“simply”overridetheencodeBegin(),encodeChildren(),(andgetRendersChildren()),

Page 525: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

andencodeEnd()oftheDataListcomponentbutinsteadcreatea“whole”rendererimplementation.Themainreasonis:simplicityandextensibility.ThosemethodsareonUIComponentspecifiedtodomorethanonlyrendering.TheyalsocheckiftheUIComponent#isRendered()returnstrue.encodeBegin()alsofiresPreRenderComponentEventandpushesthecurrentcomponentintotheEL(ExpressionLanguage)scopeas#{component}.encodeEnd()pops#{component}outoftheELscope.Andtheyalsocheckifthere’sarendererattachedand,ifso,delegatetoit.That’sallspecifiedintheirJavadoc.You’dneedtomanuallytakecareofthemyourselfwhenyouoverridethosemethods.That’sunnecessarilyrepeatedwork.And,whensomeoneinthefuturewantstoadjusttherenderingofthecomponent,theywon’tbeabletoplugacustomrendererifyourcomponentdoesn’tcheckforit.

ExtendingExistingComponentImaginethatthere’sanexistingcomponentwhosebehavioryou’dliketoadjust,usuallybyaddingoneormorenewcustomattributes.Ifthoseattributesarepurelyoutput-only,thenyoucouldjustmakeuseofthepassthroughattributesfeaturewhichwasintroducedinJSF2.2.Previously,anycustomattributewhichwasn’tofficiallysupportedbythecomponentwassimplyignoredduringviewrendertime.Forexample,whenyou’dliketoaddthemissingacceptattribute totheexisting<h:inputFile>component,simplyaddingtheattributeasfollowswon’twork.

<h:inputFileid="photo"value="#{editProfile.photo}"

5 6

Page 526: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

accept="image/*"/>

WiththepassthroughattributesfeatureyoucouldexplicitlyinstructJSFtorenderthecustomattributeanyway.Thiscanbedoneintwoways.Thefirstwayisregisteringitviathehttp://xmlns.jcp.org/jsf/passthroughXMLnamespace.

<...xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:a="http://xmlns.jcp.org/jsf/passthrough">

...

<h:formenctype="multipart/form-data">

<h:inputFile...a:accept="image/*"/>

</h:form>

Anotherwayisdeclaringitviathe<f:passThroughAttribute>tag.

<...xmlns:f="http://xmlns.jcp.org/jsf/core"

xmlns:h="http://xmlns.jcp.org/jsf/html">

...

<h:formenctype="multipart/form-data">

<h:inputFile...>

<f:passThroughAttributename="accept"

value="image/*"/>

</h:inputFile>

</h:form>

It’lljustworkfineontheclientsideeitherway.Onthebrowserssupportingthisattribute,thefilebrowsedialogwillonlyshowthefilesmatchingthecommaseparatedIANA(InternetAssignedNumbersAuthority)mediatypesspecifiedintheacceptattribute.However,thiswon’tworkinbrowsersnotsupportingthisattribute, norwillitvalidate

7

8

Page 527: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

anythingontheserverside.Evenifthebrowsersupportsit,anymalicious-mindedendusercaneasilymanipulatetheretrievedHTMLdocumentandremovetheacceptattributeandhencebeabletouploadadifferentfiletype.

Forexactlythatserver-sidework,you’dliketoextendthe<h:inputFile>componenttoperformvalidationbasedontheacceptattribute.ThefirststepislookingatwhichUIComponentclassexactlyisrepresentedby<h:inputFile>.AsyoucanseeinTable11-1,that’sjavax.faces.component.html.HtmlInputFile.Let’sstartbyextendingitandaddingthenewacceptattribute.

@FacesComponent(createTag=true)

publicclassInputFileextendsHtmlInputFile{

@Override

publicvoidencodeBegin(FacesContextcontext)throws

IOException{

Stringaccept=getAccept();

if(accept!=null){

getPassThroughAttributes().put("accept",accept);

}

super.encodeBegin(context);

}

publicStringgetAccept(){

return(String)getStateHelper().eval("accept");

}

publicvoidsetAccept(Stringaccept){

getStateHelper().put("accept",accept);

}

}

Page 528: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Notethatthere’snopropertyfortheacceptattribute.Anypubliccomponentattributemustberepresentedbyagetter/setterpairwhichdelegatesfurthertoUIComponent#getStateHelper().Basically,youmustdelegateallview-scopedcomponentattributestotheStateHelper.ThiswillinturntakecarethattherightdeltasendupintheJSFviewstate.Thisisofcourseoptional,butnotdoingsowillmakethecomponentinstancenotprogrammaticallymanipulatable.AnychangesperformedduringapreviousHTTPrequestwouldgetlostduringthesubsequentHTTPpostbackrequestfortheverysimplereasonthattheUIComponentinstanceisrecreatedfromscratch.

AlsonotethatthenewacceptattributeissimplyaddedasapassthroughattributeintheencodeBegin()methodbeforedelegatingtothesuperclassmethodwheretheactualrenderingjobtakesplace.Thisremovestheneedtocreateawholecustomrendererfortheparticularpurposeofrenderinganewattribute.Let’stestitnow.

<...xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:my="http://xmlns.jcp.org/jsf/component">

...

<h:formenctype="multipart/form-data">

<my:inputFileid="photo"value="#{editProfile.photo}"

accept="image/*"required="true">

<f:ajaxlistener="#{editProfile.upload}"

render="photo_m"/>

</my:inputFile>

<h:messageid="photo_m"for="photo"/>

</h:form>

Forthesakeofcompleteness,here’swhatthebackingbeanlookslike.

Page 529: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Named@RequestScoped

publicclassEditProfile{

privatePartphoto;

publicvoidupload(){

StringfileName=photo.getSubmittedFileName();

StringfileType=photo.getContentType();

longfileSize=photo.getSize();

System.out.println("Filename:"+fileName);

System.out.println("Filetype:"+fileType);

System.out.println("Filesize:"+fileSize);

}

//Add/generategetterandsetter.

}

NowwehavebasicallycreatedtheUIComponentequivalentof<h:inputFile>withapassthroughacceptattribute.Thenextstepisimplementingserver-sidevalidationofwhetherthemediatypeoftheuploadedfilematchesthespecifiedacceptattribute.Forthis,we’dliketooverrideUIInput#validateValue()inourInputFileclassasbelow.Thisrunsduringtheprocessvalidationsphase(thirdphase).

@Override

protectedvoidvalidateValue(FacesContextcontext,Object

newValue){

Stringaccept=getAccept();

if(accept!=null&&newValueinstanceofPart){

Partpart=(Part)newValue;

StringcontentType=context.getExternalContext()

.getMimeType(part.getSubmittedFileName());

Page 530: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

StringacceptPattern=accept.trim()

.replace("",".").replaceAll("\\s*,\\s*","|");

if(contentType==null||

!contentType.matches(acceptPattern)){

Stringmessage="Unacceptablefiletype";

context.addMessage(getClientId(context),new

FacesMessage(

FacesMessage.SEVERITY_ERROR,message,null));

setValid(false);

}

}

if(isValid()){

super.validateValue(context,newValue);

}

}

Asyousee,itwillbasicallycheckiftheacceptattributeisspecifiedandifthere’sasubmittedfile,andifsothenconverttheacceptattributetoaregularexpressionpatternandmatchthecontenttypeofthesubmittedfileagainstit.TheacceptattributerepresentsacommaseparatedstringofIANAmediatypeswhereintheasteriskisusedasawildcardandthecommaisusedasadisjunctionoperator.Anexampleacceptvalueof"image/*,application/pdf"isthiswayconvertedtoaregularexpressionof"image/.*|application/pdf".Ifitdoesn’tmatch,thenitwilladdafacesmessageassociatedwiththecomponenttothefacescontextandmarkthecomponentasinvalidbycallingUIInput#setValid()withfalse.Intheend,ifthecomponentisvalid,itwillcontinuethevalidationcalltothesuperclass.

Furtherthere’sanotherthingtomention:thecontenttype

Page 531: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

isnotobtainedfromPart#getContentType()butfromExternalContext#getMimeType()basedonthesubmittedfilename.Thisisjusttocoverthecornercasethattheclientdoesn’tsendacontenttypealong,orevensendsonewhichisnotunderstoodbytheserver.ExternalContext#getMimeType()basicallyobtainsthelistofknowncontenttypesfrom<mime-mapping>entriesinweb.xml.Theserveritselfhassomedefaultvaluesandyoucanoverrideorextendtheminthewebapplication’sownweb.xml.

Nowthefile’scontenttypeattributeisfilteredontheclientsideandvalidatedontheserverside.Allgoodandwell,butthisofcourseonlyvalidatesthefile’scontenttypebasedonthefilenameandnotthefile’sactualcontent.ImaginethatonecreatesaZIPfileandsimplyrenamesthefileextensiontobecomeanimagefile,orevenanexecutablefilewithmalware.Itwouldstillpassthroughthefiletypevalidationonboththeclientandserverside.Frankly,thisresponsibilityisnotuptothecomponentitself,buttoyou,theJSFdeveloper.Thecorrectsolutionwouldbetocreateacustomvalidatorandattachittothecomponent.Here’swhatsuchanimagefilevalidatorcanlooklike,withalittlehelpfromtheJava2DAPIwhichiscapableofparsingimagefiles.Ifitthrowsanexceptionorreturnsnull,thenit’sverydefinitelynotanimagefile.

@FacesValidator("project.ImageFileValidator")

publicclassImageFileValidatorimplementsValidator<Part>{

@Override

publicvoidvalidate

(FacesContextcontext,UIComponentcomponent,Part

Page 532: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

value)

throwsValidatorException

{

if(value==null){

return;//Let@NotNullorrequired="true"

handle.

}

try{

ImageIO.read(value.getInputStream()).toString();

}

catch(Exceptione){

Stringmessage="Notanimagefile";

thrownewValidatorException(new

FacesMessage(message),e);

}

}

}

Inordertogetittorun,declareitasvalidatorattributeofthecomponenttag.

<my:inputFile...validator="project.ImageFileValidator"/>

Itworksbeautifully.Now,whenvalidationpassesaswell,thebackingbeanactionmethodisinvokedwhereinyoucansavetheuploadedfiletothedesiredlocation.Thiscouldbeimplementedasfollows:

publicvoidupload(){

Pathfolder=Paths.get("/path/to/uploads");

StringfileName=Paths.get(photo.getSubmittedFileName())

.getFileName().toString();

intindexOfLastDot=fileName.lastIndexOf('.');

Stringname=fileName.substring(0,indexOfLastDot);

Stringextension=fileName.substring(indexOfLastDot);

FacesMessagemessage=newFacesMessage();

Page 533: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

try(InputStreamcontents=photo.getInputStream()){

Pathfile=Files.createTempFile(folder,name+"-",

extension);

Files.copy(contents,file,

StandardCopyOption.REPLACE_EXISTING);

message.setSummary("Uploadedfilesuccessfully

saved.");

}

catch(IOExceptione){

message.setSummary("Couldnotsaveuploadedfile,try

again.");

message.setSeverity(FacesMessage.SEVERITY_ERROR);

e.printStackTrace();

}

FacesContext.getCurrentInstance().addMessage(null,

message);

}

Youmightwonderwhyitseemstosavetheuploadedfileasatemporaryfile.Thisisactuallynottrue.We’rejustutilizingtheFiles#createTempFile()facilityinordertoguaranteetheuniquenessofthefilenameofthesavedfile.Itwillautomaticallyincludeauniquerandomstringbetweenthefilenameandthefileextension.Otherwise,whenmultiplepeopleuploaddifferentfileswithcoincidentallythesamename,theymayoverwriteeachotherandwe’dloseinformation.

ExtendingExistingRendererImaginethatthere’sanexistingrendererwhichhaslogicbugsorshortcomingsandyou’dliketoquicklypatchitbyextendingitinsteadofrewritingfromscratch.Unfortunately,

Page 534: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

itsoundsfareasierthanitactuallyis.Thatis,standardrendererimplementationsarenotpartofthestandardJSFAPI,contrarytostandardHTMLcomponentimplementationswhichareavailableinthejavax.faces.component.htmlpackage.TheactualstandardHTMLrendererimplementationsareprovidedbytheJSFimplementationitself.Mojarrahastheminthecom.sun.faces.renderkit.html_basicpackageandMyFaceshasthemintheorg.apache.myfaces.renderkit.htmlpackage.

AnotherproblemwiththosestandardHTMLrenderersisrelativelypoorabstractionofthecode.BasicallyallthosestandardHTMLrenderersdon’thaveabstracted-outpiecesofcodewhichsolelyemitHTMLmarkupinsuchwaythatit’sfullyseparatedfromthelogic.Inotherwords,whenyouneedtofixsomelogic,you’dalmostalwaysalsohavetowriteorcopy/pasteallthecoderesponsibleforemittingHTML.

Acommonreal-worldexampleisthedesiretolet<h:message>or<h:messages>renderthefacesmessageunescaped,sothatyoucanembedsomeHTMLcodeinafacesmessage,morethanoftentoprovidealinktothedesiredtargetpage(e.g.,“Unknownuser,perhapsyouwantto<ahref="login">Login</a>?”).Thestandard<h:message>componentdoesn’tsupportsuchfacilityandtheHTML-escapingiscontrolledbyitsrendererwhichinturnisthusJSFimplementationdependent.ThisJSFbuilt-inHTMLescapingisfoundoverallplaceandisaveryimportantguardagainstpossibleXSSattackholeswhenyou’reabouttoembeduser-controlleddatainthewebpage.Thereareahandfulofcomponentswhichhavean

Page 535: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

explicitattributetoturnoffthisHTML-escaping,suchas<h:outputText>withitsescapeattribute,<f:selectItem>withitsitemEscapedattribute,and<f:selectItems>withitsitemLabelEscapedattributes.Suchanattributeis,however,absentin<h:message>and<h:messages>.SeealsoJSFspecissue634. PerhapsitwillbeaddedinJSF.next,butfornowyoucan’tgoaroundathird-partycomponentlibraryorextendingtheexistingstandardHTMLrenderer.

We’lltakethatasanexampletoextendanexistingstandardHTMLrendererfor<h:message>.Thefirststepislookingatwhichrendererexactlyiscurrentlyusedbythe<h:message>component.InTable11-1youwillseethatthiscomponentisbackedbytheHtmlMessageclass.Thecurrentlyusedrendererimplementationisprogrammaticallyasfollows:StringcomponentFamily=HtmlMessage.COMPONENT_FAMILY;

StringrendererType=new

HtmlMessage().getRendererType();

Rendererrenderer=

FacesContext.getCurrentInstance().getRenderKit()

.getRenderer(componentFamily,rendererType);

System.out.println(renderer.getClass());

Incaseyou’reusingMojarraasJSFimplementation,it’llprintasfollows:classcom.sun.faces.renderkit.html_basic.MessageRenderer

That’sthusexactlytherendererclasswe’dliketoextend.Mojarraisopensourceanditssourcecodeiscurrentlyavailableat

9

Page 536: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

https://github.com/javaserverfaces/mojarra

.Onceyou’vegottentheMessageRenderersourcecodeathands,thenextstepistofigureoutwhereexactlythesummaryanddetailoftheFacesMessageisbeingrenderedandhowexactlywecanoverrideitwithaminimumofcode.WecanseeinthesourcecodethatittakesplaceintheencodeEnd()methodwhich,inthecurrentMojarra2.3.3version,isalready182linesofcode.It’susingResponseWriter#writeText() torenderthesummaryanddetail.We’dliketoreplacethisbyResponseWriter#write()sothatitdoesn’tperformanyescaping.

Wecanofcourseextendtheclass,copy/pasteallthe182linesoftheencodeEnd()method,andadjustthewriteText()callsforthesummaryaswellasdetailvariablesasfollows:Objectescape=component.getAttributes().get("escape");

if(escape==null||

Boolean.parseBoolean(escape.toString())){

writer.writeText(summary,component,null);

}

else{

writer.write(summary);

}

However,thisisnotterriblyelegant.WhatifweweretocaptureallwriteText()callsduringtheencodeEnd()methodandtransparentlydelegatetowrite()?Thatwouldlookmuchbetter.YoucanachievethisbywrappingtheResponseWriter,settingitonthefacescontext,and

10

Page 537: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

passingitthroughthesuperclass.AlmostanypublicJSFAPIartifacthasanequivalentXxxWrapperclassaswellintheAPI.Youcanfindthemallinthe“Allknownimplementingclasses”sectionofthejavax.faces.FacesWrapperJavadoc. AllthosewrapperclassesmakeJSFveryeasilycustomizableandextensible.Allofthemhaveaconstructortakingtheto-be-wrappedclassandyoubasicallyjustneedtopickoneormoreofthemethodsyou’dliketodecorate.

Allinall,here’showwecouldextendtheMessageRenderertodelegateallwriteText()callsduringtheencodeEnd()methodtowrite().

publicclassEscapableMessageRendererextendsMessageRenderer

{

@Override

publicvoidencodeEnd

(FacesContextcontext,UIComponentcomponent)

throwsIOException

{

ResponseWriterwriter=context.getResponseWriter();

try{

context.setResponseWriter(new

ResponseWriterWrapper(writer){

@Override

publicvoidwriteText

(Objecttext,UIComponentcomponent,

Stringproperty)

throwsIOException

{

Stringstring=text.toString();

Objectescape=component.getAttributes()

.get("escape");

if(escape==null

||

11

Page 538: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Boolean.parseBoolean(escape.toString()))

{

super.writeText(string,component,

property);

}

else{

super.write(string);

}

}

});

super.encodeEnd(context,component);

}

finally{

context.setResponseWriter(writer);

}

}

}

Donotethatit’sveryimportanttorestoretheoriginalresponsewriterinthefinallyofthetryblockwhereinthewrappedresponsewriterisbeingused.Inordertogetittorun,registeritasfollowsinfaces-config.xmlonthecomponentfamilyandrenderertypeasassociatedwiththe<h:message>component:<renderkit><renderer>

<component-

family>javax.faces.Message</component-family>

<renderer-type>javax.faces.Message</renderer-

type>

<renderer-class>

com.example.project.renderer.EscapableMessag

eRenderer

</renderer-class>

</renderer>

Page 539: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</renderkit>

No,[email protected]’tworkwhenextendinganexistingrenderer.Theoriginalrenderersarebythemselvesalreadyregisteredontheverysamecomponentfamilyandrenderertype,somewhereinanXMLfile.Andyouknow,anXML-basedconfigurationalwaysgetshigherprecedenceoveranannotation-basedconfigurationwhenbotharediscovered.

Nowyoucanjustsettheescapeattributeoftheexisting<h:message>componenttofalseinordertogettheextendedrenderertodoitsjob.

<h:message...escape="false"/>

Bewarethatyoudon’tembeduser-controlledinputinanyfacesmessagewhichgetsdisplayedinthere,oryou’llopenupapotentialXSSattackhole.

CustomTagHandlersInChapter3,youlearnedaboutthedifferencebetweentheviewbuildtimeandtheviewrendertime,andthattaghandlerssuchasJSTLrunwhilebuildingtheJSFcomponenttreewhileJSFcomponentsrunwhileprocessingtheHTTPrequestandresponsethroughtheJSFlifecycle.NotonlycanJSFcomponentsbecustomizedbutalsotaghandlers.ThisisparticularlyusefulwhenyouwanttocontrolthebuildingoftheJSFcomponenttreeinsteadofprocessingtheHTTPrequestandresponse.

<f:viewParam>isusefulonmaster-detailpages.From

Page 540: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

themasterpage,youcanlinktothedetailpagewiththeentityIDastheparameter.Inthedetailpage,youcanloadtheentitybyIDvia<f:viewParam>.Itgoesasfollows:<f:metadata>

<f:viewParamname="id"value="#{editItem.item}"

converter="project.ItemConverter"

converterMessage="Unknownitem"

required="true"requiredMessage="Badrequest">

</f:viewParam>

</f:metadata>

Whenconversionorvalidationfails,afacesmessagewillbeaddedtothefacescontextofthecurrentpage.However,moreoftenyou’djustliketodirectlyredirecttheuserbacktothemasterpage.Thisisrelativelytrivialtoimplementwith<f:event>onPostValidateEvent.No,<f:viewAction>won’tworkasthatwouldn’tbeinvokedinfirstplacewhenthere’saconversionorvalidationerror.

<f:metadata>

...

<f:eventtype="postValidate"listener="#

{editItem.onload()}"/>

</f:metadata>

Whereintheonload()methodlooksasfollows:publicvoidonload()throwsIOException{

FacesContextcontext=

FacesContext.getCurrentInstance();

if(context.isValidationFailed()){

ExternalContextec=

context.getExternalContext();

Page 541: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ec.redirect(ec.getRequestContextPath()+

"/items.xhtml");

}

}

Okay,thatworks,butthiswillendupinboilerplatecodewhenyouhavemoreofsuchpages.Ideally,you’dliketobeabletodeclarativelyregisteraneventlisteneron<f:viewParam>itselfinaself-documentingwaylikebelowsothatyoucankeepthebackingbeancodefreeofmanualrequest-responseprocessingclutter.

<f:metadata>

<f:viewParam...>

<t:viewParamValidationFailedredirect="/items.xhtml"

/>

</f:viewParam>

</f:metadata>

ThiscanbeachievedwithataghandlerwhichbasicallyregistersanewsystemeventlistenerontheUIViewParametercomponentrepresentedbythe<f:viewParam>tag.Thetaghandlerclassmustextendfromjavax.faces.view.facelets.TagHandler.

publicclassViewParamValidationFailedextendsTagHandler

implementsComponentSystemEventListener

{

privateStringredirect;

publicViewParamValidationFailed(TagConfigconfig){

super(config);

redirect=

getRequiredAttribute("redirect").getValue();

}

Page 542: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Override

publicvoidapply(FaceletContextcontext,UIComponent

parent)

throwsIOException

{

if(parentinstanceofUIViewParameter

&&!context.getFacesContext().isPostback())

{

parent.subscribeToEvent(PostValidateEvent.class,

this);

}

}

@Override

publicvoidprocessEvent(ComponentSystemEventevent)

throwsAbortProcessingException

{

UIComponentparent=event.getComponent();

parent.unsubscribeFromEvent(PostValidateEvent.class,

this);

FacesContextcontext=event.getFacesContext();

if(context.isValidationFailed()){

try{

ExternalContextec=

context.getExternalContext();

ec.redirect(ec.getRequestContextPath()+

redirect);

}

catch(IOExceptione){

thrownewAbortProcessingException(e);

}

}

}

}

Indeed,thisalsoimplementsjavax.faces.event.ComponentSystemEventList

Page 543: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ener.Thisisnotstrictlyrequiredforataghandler;it’sjustdoneforcodeconvenienceinthisspecificexample.Theoverriddenapply()methodisforTagHandlerandtheoverriddenprocessEvent()methodisforComponentSystemEventListener.Intheapply()methodwehavetheopportunitytoprogrammaticallymanipulatetheparentcomponent,beforeit’sbeingaddedtothecomponenttree.

Wecanprogrammaticallyachievethesamebehavioras<f:event>bycallingUIComponent#subscribeToEvent(),passingthecomponentsystemeventtypeandlistenerinstanceofinterest.Thelistenerinstanceofinteresthappenstobejustthecurrenttaghandlerinstance.Whenthecomponentsystemeventofinteresthasbeenpublishedbytheapplication,thentheprocessEvent()methodofthelistenerinstancewillbeinvoked.

ThefirstthingwedoinprocessEvent()istounsubscribethelistenerinstance.ThisisdoneonpurposebecausecomponentsystemeventlistenersareconsideredstatefulandthereforeareinherentlysavedintheJSFviewstate.AneasywaytoobservethisistoreconfigureJSFtosavetheviewstateontheclientsidebyexplicitlysettingthejavax.faces.STATE_SAVING_METHODcontextparametertoclientinweb.xmlandinspectingthesizeofthejavax.faces.ViewStatehiddeninputfieldinthegeneratedHTMLoutputofanyJSFform.Everytimeyouadd<f:event>,ordon’tunsubscribeComponentSystemEventListenerafterithasdoneitsjob,thesizeoftheJSFviewstategrowswiththeserialized

Page 544: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

formofthelistenerinstance.Inthisspecificusecaseofalistenerwhichshouldonlyrunduringanon-postback,that’sjustunnecessary;hencetheexplicitunsubscribe.

Now,inordertogetittorun,registeritinWEB-INFexample.taglib.xmlasfollows:<tag><tag-name>viewParamValidationFailed</tag-name>

<handler-class>

com.example.project.taghandler.ViewParamValidati

onFailed

</handler-class>

</tag>

PackaginginaDistributableJARIncaseyouhavedevelopedabunchofreusablecomponents,renderers,taghandlers,tagfiles,compositecomponents,andwhatnot,andyou’dliketopackageitinaJARfileforinclusioninWEB-INFlibofawebapplication,thenyouneedtocreateaso-calledwebfragmentproject.BasicallyallJSF-orientedcomponentsandutilitylibrariessuchasOmniFaces,PrimeFaces,OptimusFaces,BootsFaces,ButterFaces,andDeltaSpikearebuiltlikethis.InMavenperspective,it’smerelyaJARproject.Thekeyistoputfileswhichyounormallyputinthesrc/main/webappfolderofaMavenWARprojectinthesrc/main/resources/META-

INF/resources/[libraryName]folderoftheMavenJARproject.Thereisonemainexception,alldeploymentdescriptorfileswhichyounormallyputinsrc/main/webapp/WEB-INF,suchasweb.xml,

Page 545: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

faces-config.xml,*.taglib.xml,andbeans.xmlgodirectlyinthesrc/main/resources/META-INFfolder.Anotherexceptionisthattheweb.xmlfileistobereplacedbyweb-fragment.xml.

The[libraryName]subfolderintheresourcesfolderrepresentsthe“libraryname”whichisgenerallytheURL-friendlyformoftheprojectname.Forexample,“omnifaces”,“primefaces”,“optimusfaces”,“bsf”,“butterfaces”,etc.Exactlythislibrarynameisthenusableinthelibraryattributeofresourcecomponentssuchas<h:outputScript>,<h:outputStylesheet>,and<h:graphicImage>.BelowiswhatsuchaMavenJARprojectlookslikeinEclipsewhenorganizedtoconformtothe“webfragment”rules.Noteparticularlythestructureofthesrc/main/resourcesfolder.Ofcourse,anyJavaclassescangoinsidethesrc/main/javafolderintheusualway.

Page 546: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Wherebythecommon.taglib.xmllookslikethefollowing,withthecompositelibrarynamesettoapathrelativetosrc/main/resources/META-INF/resourcesandthetagfilesourcesettoapathrelativetothelocationofthe*.taglib.xmlfileitself.

<?xmlversion="1.0"encoding="UTF-8"?>

<facelettaglib

xmlns:="http://xmlns.jcp.org/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee

Page 547: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

http://xmlns.jcp.org/xml/ns/javaee/web-

facelettaglibrary_2_3.xsd"

version="2.3"

>

<namespace>http://example.com/common</namespace>

<short-name>common</short-name>

<composite-library-name>common/components</composite-

library-name>

<tag>

<tag-name>someTag</tag-name>

<source>resourcescommontags/someTag.xhtml</source>

</tag>

</facelettaglib>

Andwherebytheweb-fragment.xmllooksasfollows,nearlyidenticaltoweb.xml,onlywithadifferentrootelement,<web-fragment>insteadof<web-app>.

<?xmlversion="1.0"encoding="UTF-8"?>

<web-fragment

xmlns:="http://xmlns.jcp.org/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee

http://xmlns.jcp.org/xml/ns/javaee/web-

fragment_4_0.xsd"

version="4.0"

>

<name>common</name>

</web-fragment>

OncesuchawebfragmentprojectisbuiltasaJARfileandincludedinWEB-INFlibofthemainwebapplicationproject,thentheresourcesoftheJARareavailableintheFaceletsfilesofthemainwebapplicationprojectviathelibraryname“common”asfollows:<ui:composition

Page 548: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

template="commontemplates/some.xhtml"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

xmlns:common="http://example.com/common"

>

<ui:definename="content">

<common:someComposite/>

<h:graphicImagelibrary="common"

name="js/some.svg"/>

<ui:includesrc="commonincludes/some.xhtml"/>

<h:outputScriptlibrary="common"

name="scripts/some.js"/>

<h:outputStylesheetlibrary="common"

name="styles/some.css"/>

<common:someTag/>

</ui:define>

</ui:composition>

ResourceDependenciesTheremaybecaseswherebyyourcustomcomponentorrendererdependsonaspecificJavaScriptorStylesheetresource,forwhichyouwouldliketoavoidtheenduserhavingtomanuallyincludeitvia<h:outputScript>or<h:outputStylesheet>.Insuchcases,[email protected]

yannotation useful.Imaginethatyouwouldliketoautomaticallyincludecommon:scripts/some.jsandcommon:styles/some.cssalongwithaparticularcustomcomponent;thenyoucandosoasfollows:

12

Page 549: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@ResourceDependency(library="common",name="some.css",

target="head")

@ResourceDependency(library="common",name="some.js",

target="head")

publicclassSomeCustomComponentextendsUIComponent{

//...

}

YoucanofcoursealsoincludeJSF’sownjavax.faces:jsf.jswhennecessary,i.e.,whenyourcustomcomponenthappenstorelyon,forexample,thejsf.ajax.request()orotherfunctionsprovidedbythestandardJSFJavaScriptAPI.javax.faces:jsf.jscanbeincludedasfollows,withthescriptlibraryandnameavailableasconstantsofResourceHandler.

@ResourceDependency(

library=ResourceHandler.JSF_SCRIPT_LIBRARY_NAME,

name=ResourceHandler.JSF_SCRIPT_RESOURCE_NAME,

target="head")

publicclassSomeCustomComponentextendsUIComponent{

//...

}

Footnotes1https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/UIData.html#getRowCo

unt--.

2https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/UIData.html#setRowIn

dex-int-.

Page 550: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

3https://javaee.github.io/javaee-

spec/javadocs/javax/faces/model/DataModel.html.

4https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/UIData.html#getValue

--.

5https://developer.mozilla.org/en-

US/docs/Web/HTML/Element/input/file.

6

https://javaserverfaces.github.io/docs/2.3/vdldocs/facel

ets/h/inputFile.html.

7www.iana.org/assignments/media-types/media-types.xhtml.

8https://caniuse.com/#feat=input-file-accept.

9https://github.com/javaee/javaserverfaces-

spec/issues/634.

10https://javaee.github.io/javaee-

spec/javadocs/javax/faces/context/ResponseWriter.html#wr

iteText-java.lang.Object-

javax.faces.component.UIComponent-java.lang.String-.

11https://javaee.github.io/javaee-

spec/javadocs/javax/faces/FacesWrapper.html.

12https://javaee.github.io/javaee-

spec/javadocs/javax/faces/application/ResourceDependency

.html.

Page 551: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1) (2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_12

12.SearchExpressions

BaukeScholtz andArjanTijms

Willemstad,Curaçao Amsterdam,Noord-Holland,TheNetherlands

Asstatedinthesections“AjaxLifeCycle”inChapter3and“AjaxifyingComponents”inChapter4,theexecuteandrenderattributesof<f:ajax>tagtakeaspace-separatedcollectionofso-calledcomponentsearchexpressions.

ThesearchexpressionshavealwaysbeenpartofJSF(JavaServerFaces)sincethebeginningasthisisusedintheforattributeof<h:outputLabel>,<h:message>,and<h:messages>,buttheyhaveonlybecomeessentialknowledgefortheJSFdevelopersincetheintroductionof<f:ajax>inJSF2.0.Namely,labelsandmessagesareinalmostanycasealreadywithintheverysamenamingcontainerparentasthetargetinputcomponent,sosimplyspecifyingtheIDofthetargetinputcomponentintheforattributealreadysuffices,butthisisnotnecessarilytruefortheexecuteandrenderattributesof<f:ajax>asthetargetcomponentmaysitinadifferentnamingcontainercontextoreveninaphysicallydifferentFaceletsfile.

Toovercomethesedifficulties,JSF2.0introducedafew

1 2

Page 552: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

moreabstractsearchexpressions:"@this","@form","@all",and"@none".Somethinglike"@form"isparticularlyeasytouse,asitjustmeanstargetwhateverthecurrentformis.Ifthatcurrentformisdefinedtwoparenttemplatesupfromthepagewhereit’sreferenced,thisreallymakesreferencingitmucheasier.

Althoughtheymadethingsmucheasier,thesekeywordswerequitelimited.Notonlyaretherejustfourofthem,butthey'realsonotextensibleandthedefaultJSFcomponentsetonlyusestheminternallyinthe<f:ajax>tag.Usingtheminothercomponents,eveninJSF’sown<h:outputLabel>,<h:message>,and<h:messages>,aswellasusingthemprogrammatically,wasleftout.Therefore,inJSF2.3a"ComponentSearchExpressionFramework"wasatthelastmomentintroducedthatgreatlyexpandsuponthosefourkeywords.ItwaslargelybasedonaprovenAPI(applicationprogramminginterface)ofPrimeFaces.

RelativeLocalIDsThisisthesimplestformofacomponentsearchexpression.Themostcommonusecasesarefoundintheforattributeof<h:outputLabel>,<h:message>,and<h:messages>components.ItsimplyreferencesthesoleIDofthetargetUIInputcomponent.

<h:outputLabelfor="email"value="Emailaddress"/>

<h:inputTextid="email"value="#{login.email}"

required="true"/>

<h:messagefor="email"/>

1

Page 553: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Thisonlyprerequiresthatthetargetcomponentisalsosittingwithintheverysamenamingcontainerparent.AnamingcontainerparentisacomponentthatimplementstheNamingContainerinterface. InstandardJSF,only<h:form>,<h:dataTable>,<ui:repeat>,and<f:subview>areaninstanceofNamingContainer.AllcompositecomponentsarealsoaninstanceofNamingContainer,buttagfilesarenot.

IncaseyouneedtoreferenceaspecificUIInputcomponentwithinanamingcontainerfrom<h:outputLabel>on,thenyouneedtoappendtheso-callednamingcontainerseparatorcharactertotheIDofthenamingcontainercomponentandthentheIDofthetargetUIInputcomponent.Thedefaultnamingcontainerseparatorcharacterisacolon“:”.ThecurrentlyconfiguredseparatorcharacterisprogrammaticallyavailablebyUINamingContainer#getSeparatorCharacter().

charseparatorCharacter=UINamingContainer

.getSeparatorCharacter(FacesContext.getCurrentInstance);

Thisisconfigurableviathejavax.faces.SEPARATOR_CHARcontextparameterinweb.xml.

<context-param>

<param-name>javax.faces.SEPARATOR_CHAR</param-name>

<param-value>-</param-value>

</context-param>

Caution

2

3

Page 554: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Changingthistosomethingelselikeahyphen“-”orevenanunderscore“_”isnotrecommended.

Inthelongterm,itisconfusingandbrittleasthosecharactersarealsoallowedintheIDattributeitself.JSFdoesnotvalidatethecomponentIDagainstthecurrentlyconfigurednamingcontainerseparatorcharacterandthusitmayeasilyslipthroughandcausetroublewhilereferencingsuchacomponentinasearchexpression.

ComingbacktoreferencingaspecificUIInputcomponentwithinanamingcontainerfrom<h:outputLabel>on,intheexamplecompositecomponent<t:inputLocalTime>asdemonstratedinthesection“CompositeComponents”inChapter7,thehourdropdowncomponenthasanIDof“hour”.Thus,for<h:outputLabel>,whenusingthedefaultnamingcontainerseparatorcharacter,therelativelocalIDofthehourdropdowninsidethecompositecomponentis“time:hour”.

<h:outputLabelid="l_time"for="time:hour"value="Time"/>

<t:inputLocalTimeid="time"value="#{schedule.time}"

required="true"/>

<h:messageid="m_time"for="time"/>

Notethatthisisnotnecessaryfor<h:message>asfacesmessagesareunderthehoodalreadyaddedtothefacescontextwiththeclientIDofthecompositecomponentitself.

UsingrelativelocalIDsalsoworkswithinthecontextof<h:column>of<h:dataTable>.It’stheninterpretedinthecontextofthecurrentlyiteratedrow,evenwhenthetargetcomponentissittinginanothercolumn.Thefollowing

4

Page 555: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

exampledemonstratesthat:<h:dataTableid="users"value="#{admin.users}"var="user">

...

<h:column>

<f:facetname="header">Country</f:facet>

<h:selectOneMenuid="country"value="#

{user.address.country}">

<f:selectItemsvalue="#{data.countries}"/>

<f:ajaxrender="city"/>

</h:selectOneMenu>

</h:column>

<h:column>

<f:facetname="header">City</f:facet>

<h:selectOneMenuid="city"value="#

{user.address.city}">

<f:selectItemsvalue="#

{user.address.country.cities}"/>

</h:selectOneMenu>

</h:column>

...

</h:dataTable>

Underthehood,relativelocalIDsareresolvedusingthealgorithmasdescribedintheUIComponent#findComponent()API. Thismeansthatyoucanalsoresolvethemprogrammatically.YouonlyneedtoensurethatthefindComponent()methodisinvokedonthecorrectbasecomponent,noton,forexample,UIViewRoot.

AbsoluteHierarchicalIDs

5

Page 556: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Incasethetargetcomponentisnotwithinthesamenamingcontainerparentasthecurrentcomponent,thenyouneedanabsolutehierarchicalIDinsteadofalocalrelativeID.ThekeydifferenceisthatanabsolutehierarchicalIDstartswiththenamingcontainerseparatorcharacter.ItwillthensearchforthetargetcomponentfromtheUIViewRooton.Suchconstructisoftenusedintherenderattributeof<f:ajax>whenitneedstoreferenceacomponentthatisnotlocatedinsidethesameform.

<h:formid="search">

...

<h:commandButtonid="submit"...>

<f:ajaxexecute="@form"render=":results"/>

</h:commandButton>

</h:form>

<h:panelGroupid="results"layout="block">

...

</h:panelGroup>

AlesscommonusecasewhereanabsolutehierarchicalIDisneedediswhenyouneedtoreferenceacomponentthatisinturnnestedinanothernamingcontainer—forexample,whenyouwanttoupdate<h:message>associatedwithacompositecomponentduringa<cc:clientBehavior>eventinsidethecompositecomponent.

<h:formid="form">

<h:outputLabelid="l_time"for="time:hour"value="Time"

/>

<t:inputLocalTimeid="time"value="#{schedule.time}"

required="true">

<f:ajaxrender=":form:m_time"/>

<h:messageid="m_time"for="time"/>

Page 557: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</h:form>

YoucouldarguethatthisisabugoranoversightintheJSFspecification.ThisisverytrueandshouldbeworkedonforanextversionofJSF.Anotheryetlesscommonusecaseiswhenyouneedtoupdateaspecificiterationroundofaniterationcomponent,suchas<h:dataTable>and<ui:repeat>.

<h:formid="form">

<h:dataTableid="table"value="#{bean.items}"var="item">

<h:column>

<h:panelGroupid="column1"layout="block">

...

</h:panelGroup>

</h:column>

<h:column>

<h:panelGroupid="column2"layout="block">

...

</h:panelGroup>

</h:column>

</h:dataTable>

<h:commandButtonvalue="Updatesecondrow">

<f:ajaxrender=":form:table:1:column1

:form:table:1:column2">

</f:ajax>

</h:commandButton>

</h:form>

Notethattheiterationindexiszero-basedaswithnormalJavacollectionsandarrays.Alsonotethatyoubasicallyneedtowrapthecell’scontentinanothercomponentinordertoproperlyreferencethecell’scontent,andthatyouneedtoexplicitlyspecifyeverycolumninordertoupdatetheentirerow,asdemonstratedabove.Updatingtheentirecolumnis

Page 558: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

alsopossible,butlessconvenientbecauseyoubasicallyneedtospecifythesearchexpressionforeverysinglerow.Fortunately,therenderattributecantakeanEL(ExpressionLanguage)expressionandtheELstreamAPIcanbeusedtoconcatenateabunchofstringsinthe:form:table:[i]:columnformatdependingontheamountofitemsinthetable.

<h:commandButtonvalue="Updatesecondcolumn">

<f:ajaxrender="#{bean.items.stream()

.map(i->':form:list:'+=bean.items.indexOf(i)+=

':column2')

.reduce((l,r)->(l+=r)).get()}">

</f:ajax>

</h:commandButton>

Admittedly,thisisnotthemostelegantapproach.You’dbetterdelegatetoacustomfunctioninanapplication-scopedbean.Itcouldlooksomethinglikethefollowing:<h:commandButtonvalue="Updatesecondcolumn">

<f:ajax

render="#{ajax.columnIds(bean.items,

':form:table::column2')}"

</f:ajax>

</h:commandButton>

Wherebythe#{ajax}application-scopedbeanlookssomethinglikethefollowing:@Named@ApplicationScopedpublicclassAjax{

publicStringcolumnIds(List<?>list,String

idTemplate){

Page 559: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

returnIntStream.range(0,list.size()).boxed()

.map(i->idTemplate.replace("::",":"+i+

":"))

.collect(Collectors.joining(""));

}

}

That’salreadysomethingbetter,butstillboilerplate-ish.Ifnecessary,youcanalsoprogrammaticallyaddAjaxrenderIDsfromabackingbeanon.YoucanusethePartialViewContext#getRenderIds() forthis.Thereturnedcollectionis,namely,mutableandonlyconsultedduringtherenderresponsephase(sixthphase).YoualsoneedtospecifyanabsolutehierarchicalIDhere,butwithonlyoneimportantdifference:itcannotstartwiththenamingcontainerseparatorcharacter.Inotherwords,“:form:table:0:column2”isn’tgoingtowork;youneedtospecify“form:table:0:column2”instead.It’salwaysresolvedrelativetoUIViewRoot.

FacesContextcontext=FacesContext.getCurrentInstance();

PartialViewContextajaxContext=

context.getPartialViewContext();

ajaxContext.getRenderIds().add("form:table:0:column2");

Asatip,incaseyou’rehavingahardtimefiguringouttheabsolutehierarchicalIDand/ormemorizingwhichcomponentsexactlyarenamingcontainers,thenyoucanalwayslookinthegeneratedHTMLoutput.OpentheJSFpageinyourfavoritewebbrowser,doaViewPageSource,locatetheHTMLelementrepresentationoftheJSFcomponentofinterest,takethevalueofitsIDattribute,and

6

Page 560: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

finallyprefixitwiththenamingcontainerseparatorcharacter.Also,incaseyouencounteranautogeneratedIDprefixed

withj_id,thenyouabsolutelyneedtogivetheassociatedJSFcomponentafixedID;otherwiseitsvaluewouldbeoffwhenthecomponent’spositioninthecomponenttreeissubjecttobechangedbecauseof,forexample,aconditionallyincludedcomponentsomewherebeforethepositionofthecomponentofinterest.(Seealsothesection“Text-BasedInputComponents”inChapter4.)LikerelativelocalIDs,absolutehierarchicalIDscanbeprogrammaticallyresolvedusingthealgorithmasdescribedintheUIComponent#findComponent()API. ThefollowingexampledemonstrateshowtogetholdoftheUIDatacomponentrepresenting<h:formid="form"><h:dataTableid="table">.

UIViewRootroot=

FacesContext.getCurrentInstance().getViewRoot();

UIDatatable=(UIData)root.findComponent("form:table");

StandardSearchKeywordsJSFprovidesasetofmoreabstractsearchexpressions,knownas“searchkeywords.”Theyallstartwiththe“@”character.TheycanbeusedtosubstituteafixedcomponentIDinthesearchexpression.Table12-1providesanoverviewofthem.

Table12-1 StandardSearchKeywordsProvidedbyJSF

Keyword

Resolvesto

Sinc

7

Page 561: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

e

@this

UIComponent#getCurrentComponent()

2.0

@form

UIComponent#getNamingContainer()untilanUIFormisencountered

2.0

@all

Everything

2.0

@none

Nothing

2.0

@parent

UIComponent#getParent()

2.3

@child(index)

UIComponent#getChildren()atgivenindex

2.3

@next

UIComponent#getParent()andthenUIComponent#getChildren()atnextindex

2.3

@previous

UIComponent#getParent()andthenUIComponent#getChildren()atpreviousindex

2.3

@namingcontainer

UIComponent#getNamingContainer()

2.3

Page 562: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@composite

UIComponent#getCompositeComponentParent()

2.3

@id(id)

UIComponent#findComponent()withgivenID

.

2.3

@root

FacesContext#getViewRoot()

2.3

InastandardJSFcomponentset,allsearchkeywords,includingcustomones,canbeusedinthefollowingcomponentattributes:

<f:ajaxexecute>—Specifiescomponentswhichmustbeprocessedduringtheapplyrequestvalues,processvalidations,updatemodelvalues,andinvokeapplicationphases(second,third,fourth,andfifthphases)oftheAjaxpostbackrequest.Defaultsto@this.

<f:ajaxrender>—Specifiescomponentswhichmustbeprocessedduringtherenderresponsephase(sixthphase)oftheAjaxpostbackrequest.Defaultsto@none.

<h:outputLabelfor>—SpecifiesthetargetcomponentofthegeneratedHTML<label>element.Defaultsto@none.

<h:messagefor>—Specifiesthetargetcomponentforwhichthefirstfacesmessagemustberendered.Defaultsto@none.

<h:messagesfor>—Specifiesthetargetcomponentforwhichallfacesmessagesmustberendered.Defaultsto@none.

Notethatusingsearchkeywordsis,fortheforattributeof<h:outputLabel>,<h:message>,and<h:messages>,hasonlybeenpossiblesinceJSF2.3.Inolderversions,onlyrelativeandabsoluteIDsweresupported.

Page 563: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Themostcommonlyusedsearchkeywordisundoubtedly@form.Youhavebasicallynootherchoicewhenusing<f:ajax>inaUICommandcomponentwhichissupposedtoprocesstheentireform.

<h:form>

...

<h:commandButtonvalue="Submit"...>

<f:ajaxexecute="@form"/>

</h:commandButton>

</h:form>

AlsonewsinceJSF2.3isthatsearchkeywordscanbechainedwithregularcomponentIDs.Followingisanexamplewhichexpandsontheexamplegivenbeforeinthesection“AbsoluteHierarchicalIDs.”

<h:form>

<h:outputLabelid="l_time"for="time:hour"value="Time"

/>

<t:inputLocalTimeid="time"value="#{schedule.time}"

required="true">

<f:ajaxrender="@form:m_time"/>

<h:messageid="m_time"for="time"/>

</h:form>

Andfollowingisanexamplethatupdatestheentiretablewhenarowisdeleted.

<h:form>

<h:dataTablevalue="#{products.list}"var="product">

...

<h:column>

<h:commandButtonid="delete"value="Delete"

action="#{products.delete(product)}">

Page 564: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<f:ajaxrender="@namingcontainer"/>

</h:commandButton>

</h:column>

</h:dataTable>

</h:form>

NoteparticularlythatitthusreferencestheclosestcomponentimplementingtheNamingContainerinterface,which,inthiscontext,is<h:dataTable>andthusnot<h:form>.

Astoprogrammaticresolution,searchexpressionswithkeywordscannotbeprogrammaticallyresolvedusingUIComponent#findComponent().Forthat,youneedSearchExpressionHandler#resolveComponent()

orresolveComponents() instead.SearchExpressionHandlerisinturnavailablebyApplication#getSearchExpressionHandler().YoualsoneedtocreateSearchExpressionContextbeforehandwhichwrapsthecomponenttostartsearchingfrom.

FacesContextcontext=FacesContext.getCurrentInstance();

UIComponentbase=context.getViewRoot();//Canbeany

component.

Stringexpression="@namingcontainer";

SearchExpressionContextsearchContext=

SearchExpressionContext

.createSearchExpressionContext(context,base);

SearchExpressionHandlersearchHandler=

context.getApplication()

.getSearchExpressionHandler();

handler.resolveComponent(searchContext,expression,(ctx,

found)->{

System.out.println(found);

});

8

9

Page 565: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Frankly,it’squiteverbose,butit’sJSFAPI’sown.Fortunately,utilitylibrariessuchasOmniFacesexist.

CustomSearchKeywordsTheComponentSearchExpressionFrameworkintroducedinJSF2.3alsocomeswithanAPIwhichallowsustocreatecustomsearchkeywords.Imaginethatyouhaveaformwithmultiple<h:message>components,andthatyou’dliketore-renderthemallwhensubmittingtheform.Thenyou’dbetemptedalsotouse@formintherenderattributeof<f:ajax>.

<h:form>

<h:outputLabelfor="input1".../>

<h:inputTextid="input1".../>

<h:messagefor="input1"/>

<h:outputLabelfor="input2".../>

<h:inputTextid="input2".../>

<h:messagefor="input2"/>

<h:outputLabelfor="input3".../>

<h:inputTextid="input3".../>

<h:messagefor="input3"/>

<h:commandButtonvalue="Submit"...>

<f:ajaxexecute="@form"render="@form"/>

</h:commandButton>

</h:form>

Butthisisnotterriblyefficient.Infact,italsounnecessarilyre-rendersalllabelandinputcomponentsandanyotherstaticcontentinsidetheverysameformwhichdoesn’tatallchange

Page 566: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

duringtheAjaxpostbackrequest.Thisisawasteofresources.Ideally,weshouldhaveasearchkeywordlike“@messages”whichbasicallyreferencesallmessagecomponentswithinthesameform.

<h:form>

<h:outputLabelfor="input1".../>

<h:inputTextid="input1".../>

<h:messageid="m_input1"for="input1"/>

<h:outputLabelfor="input2".../>

<h:inputTextid="input2".../>

<h:messageid="m_input2"for="input2"/>

<h:outputLabelfor="input3".../>

<h:inputTextid="input3".../>

<h:messageid="m_input3"for="input3"/>

<h:commandButtonvalue="Submit"...>

<f:ajaxexecute="@form"render="@messages"/>

</h:commandButton>

</h:form>

Notethateach<h:message>componenthasanexplicitIDset,becausewithoutanexplicitID,theywillbydefaultrendernothingtotheHTMLoutput,andthentheJSFAjaxAPIJavaScriptwouldn’tbeabletofindtheminordertoupdateitscontentbasedontheAjaxresponse.

InordertogetJSFtorecognizethenewsearchkeyword@messages,firstextendthejavax.faces.component.search.SearchKeyword

Resolver asfollows:publicclassMessagesKeywordResolverextendsSearchKeywordResolver{

10

Page 567: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Override

publicbooleanisResolverForKeyword

(SearchExpressionContextcontext,String

keyword)

{

return"messages".equals(keyword);

}

@Override

publicvoidresolve

(SearchKeywordContextcontext,UIComponentbase,

Stringkeyword)

{

UIComponentform=base.getNamingContainer();

while(!(forminstanceofUIForm)&&form!=

null){

form=form.getNamingContainer();

}

if(form!=null){

Set<String>messageClientIds=newHashSet<>

();

VisitContextvisitContext=

VisitContext.createVisitContext

(context.getSearchExpressionContext().ge

tFacesContext());

form.visitTree(visitContext,(visit,child)

->{

if(childinstanceofUIMessage){

messageClientIds.add(child.getClient

Page 568: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Id());

}

returnVisitResult.ACCEPT;

});

if(!messageClientIds.isEmpty()){

context.invokeContextCallback(new

UIMessage(){

@Override

publicString

getClientId(FacesContextcontext){

returnString.join("",

messageClientIds);

}

});

}

}

context.setKeywordResolved(true);

}

}

Itshouldbenotedthatthisapproachisalreadyslightlyhacky.Namely,theintentoftheSearchKeywordResolveristoresolveakeywordtoexactlyonecomponentwhoseclientIDwillthenbeusedtosubstitutethekeyword.ThiscomponentwillthenbepassedtoSearchKeywordContext#invokeContextCallback

().Intheaboveapproach,weinsteadcollecttheclientIDsofallUIMessagecomponentsfoundwithintheparentUIFormandthensupplyafakeUIMessagecomponentto

Page 569: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

invokeContextCallback()whowillinturncallthegetClientId()ofthefakeUIMessagecomponentwhichactuallyreturnsthedesiredcollectionofclientIDs.

ItshouldalsobenotedthatUIComponent#visitTree() isbeingusedinsteadofrecursingoverUIComponent#getChildren()inordertocollecttheclientIDsofanyUIMessagecomponent.Namely,whenplainiteratingoverchildren,youmaysoonerorlatercomeacrossaniteratorcomponentsuchas<h:dataTable>or<ui:repeat>,andifithappenstohaveonlyoneUIMessagecomponentnested,thenyou’lleffectivelyendupwithonlyoneclientID,namely,theonewithouttheiterationindex.UIComponent#visitTree()doesn’tdothat;anyiteratorcomponentinthepathwillactuallyiterateoveritsmodelvalueandvisuallygivebackmultipleUIMessagecomponents,eachwiththecorrectclientIDwiththeiterationindexincluded.

Intheend,SearchKeywordContext#setKeywordResolved()

mustbecalledwithtrueinordertoinformthesearchcontextthatthekeywordhassuccessfullybeenresolved,evenifitactuallyresolvedtonothing.Itdoesn’tactuallydoanyharmifyouforgotthis,butifyoudon’tmarkthesearchkeywordresolvedthisway,thenthesearchcontextwillcontinueconsultingallothersearchkeywordresolvers,whichmightendupbeinglessefficient.

Finally,inordertogetthenewMessagesKeywordResolvertorun,registeritinfaces-config.xmlasfollows:<application><search-keyword-resolver>

11

Page 570: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

com.example.project.MessagesKeywordResolver

</search-keyword-resolver>

</application>

Or,programmaticallyina@WebListenerasfollows:@WebListener

publicclassApplicationConfigimplements

ServletContextListener{

@Override

publicvoidcontextInitialized(ServletContextEvent

event){

FacesContext.getCurrentInstance().getApplication

()

.addSearchKeywordResolver(new

MessagesKeywordResolver());

}

}

NotethattheFacesContextmustbeavailableatthispointandhenceaServletContainerInitializerwon'tnecessarilywork,andthattheFacesServletmaynothaveservicedarequestyetandhenceregistrationinsomemanagedbeanwon'tnecessarilywork.

Footnotes1https://www.primefaces.org/search-expression-framework/

.

2https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/NamingContainer.html

.

Page 571: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

3https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/UINamingContainer.ht

ml#getSeparatorChar-javax.faces.context.FacesContext-.

4https://stackoverflow.com/q/10726653/157882.

5https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/UIComponent.html#fin

dComponent-java.lang.String-.

6https://javaee.github.io/javaee-

spec/javadocs/javax/faces/context/PartialViewContext.htm

l#getRenderIds--.

7https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/UIComponent.html#fin

dComponent-java.lang.String-.

8https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/search/SearchExpress

ionHandler.html#resolveComponent-

javax.faces.component.search.SearchExpressionContext-

java.lang.String-javax.faces.component.ContextCallback-.

9https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/search/SearchExpress

ionContext.html.

10https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/search/SearchKeyword

Resolver.html.

11https://javaee.github.io/javaee-

spec/javadocs/javax/faces/component/UIComponent.html#vis

itTree-javax.faces.component.visit.VisitContext-

javax.faces.component.visit.VisitCallback-.

Page 572: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1) (2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_13

13.Security

BaukeScholtz andArjanTijms

Willemstad,Curaçao Amsterdam,Noord-Holland,TheNetherlands

Securityforwebapplicationsisabroadfieldandincludesanumberoftopicssuchasprotectingaccesstoresources,shieldingagainstinjectionattacksofvariouskinds,andpreventingusersfrombeingtrickedintodoingmaliciousactionsonbehalfofanattacker.

JSF(JavaServerFaces)supportsthesetopicsinvariousways,eitherbyprovidingnativesolutionsorbyintegratingwiththeJavaEEplatform’sfacilities.Foraccesstoresources,whichincludesbothauthentication(thecallerprovingitsidentity)andauthorization(thesystemdeterminingtowhichresourcesthecallerhasaccess)JSF(JavaServerFaces)integrateswiththeJavaEESecuritymachinery,whichinturnisdefinedby,amongothers,theServletspecandtheJavaEESecurityspec.

JavaEESecurity(JSR375)issupportedbyboththeWeb-andFullprofileinJavaEE8.Additionally,thereferenceimplementationSoteriaworksonJavaEE7servers,andbecauseit’sbuiltonJASPIC(JavaEEauthenticationSPI)

1 2

Page 573: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(JSR196),italsoworksonServletcontainersthatsupportJASPIC(suchasTomcatsince8.5andJettysince7.0).

JavaEESecurityOverviewandHistoryInJavaEE,securityisnotspecifiedinasinglespecificationbutisinfactspreadovermultiplespecificationsthatintegratewitheachotherinvariousways.Whilethisallowsfordifferentaspectsofsecuritytobeevolvedattheirownpace,andpracticallyspeakingevenallowssometobereplacedbynon-specimplementations,itdoesmuddythewatersofwhatspecisresponsibleforwhat.

Inthisintroductorysectionwe'llstartoffwithprovidingasomewhatbroadoverviewofwhatsomeofthepiecesareandhowtheyfittogether.We'llfocusonthewebaspectsofsecurity.SecurityisalsopresentinthingslikeEJB(EnterpriseJavaBeans)andJCA(JavaConnectorArchitecture)connectors,buttheseareoutsidethescopeofthisbook.

Historically,securityinJavaEEismostlybasedonthesecuritymodelintroducedbytheServletspec.Thatis,amodelwherethecoreelementsarean"authenticationmechanism"(FORM,BASIC,...),asetofsecurityconstraintsinweb.xmlwhereaspecificpattern(theURLpattern)iscombinedwithacollectionofroles(includingtheemptycollection),andafewwaystoprogrammaticallytestthecaller'sdetails,suchasHttpServletRequest.isUserInRole.

Whileeffective,manydetailswereleftoutintheearlydays.TheServletspecdidaskforimplementations(Servletcontainers)tobeextendiblewithrespecttotheauthenticationmechanismsbutdidnotspecifyhowexactlythisshouldbe

Page 574: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

done.Likewise,theServletspecimplicitlyrequiresan"identitystore"(File,Database,LDAP,…)thatholdsthecallerdetailssuchascredentials,name,androlesbutleftalldetailsabouthowtoconfigurethesetotheServletcontainer.

TheServletspecdidnotdefineanywaytoaccessthesecurityconstraintsdefinedinweb.xmlinaprogrammaticway,ortoprogrammaticallyinfluencetheirexecution(e.g.,tomakecertainconstraintstimebased).Furthermore,theveryearlyversionsoftheServletspecdidnotspecifytheconstraintresolutioninthestrictestway,whichallowedforsomesmalldifferencesininterpretationbetweenvariousServletcontainers.

ThefirstadditionalspectoaddresstheseconcernswasJACC(JSR115),which,simplystated,dealswithauthorizationconcerns.JACCspecifieshowtheweb.xmlsecurityconstraintsshouldberepresentedincode,namely,asacollectionofPermissioninstances.JACCalsospecifieshowthesecanbeaccessed(queried)bycode,andfinallyallowsforcustomauthorizationmodulesthatcanreplaceoraugmentthelogicthatthecontainerexecutestodetermineitsaccessdecision.AperhapssomewhatdifficultthingtounderstandaboutJACCisthatit'snotsomuchsomethingthatcanbeimplementedonitsownandthenaddedtoaServletcontainer(aswecandowithJSF),butitstandardizesandmoreconciselyspecifieswhatallServletcontainersinternallyarealreadydoing.

Thesecondadditionalspectoaddresstheabove-mentionedconcernswasJASPIC(JSR196),whichdealswithauthenticationconcerns.JASPICspecifiesatwhatmomentstheServletcontainershouldcalltheauthenticationmechanism

Page 575: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

anddefinesanexplicitinterfacetypefortheseauthenticationmechanisms,socustomauthenticationmechanismscanusethisinterfaceinsteadoftheServletcontainer'sproprietaryoneandthusbeportable.JASPICsaysafewthingsabouttheidentitystore,buttoolittletobereallyusableinpractice.LikeJACC,JASPICisn'tsomethingthatcanbeimplementedindependently,butitstandardizessomethingthatallServletcontainersarealreadydoing.

BothJACCandJASPICdefinelow-levelSPIs(serverproviderinterfaces)thataremainlyintendedtobeimplementedandusedbyvendorstoprovideextensionproducts.Theyarequitebareandfairlyabstract.Assuchtheyarenot(rich)APIs(applicationprogramminginterfaces)thataretargetedatapplicationdevelopers.HereiswheretheJavaEESecurityAPI(JSR375)comesin.JavaEESecurityoffersahigher-levelandeasier-to-register(justimplementtheinterface)andeasier-to-use(CDIbased)versionoftheJASPICauthenticationmodule.TheJavaEESecurityAPIalsofullydefinestheidentitystoreartifact,butperhapsmostimportantitprovidesanumberofconcreteimplementationsofallofthese,amongwhichisaFORM-basedauthenticationmechanismoptimizedforusewithJSFandseveralidentitystoressuchasaJDBC(JavaDatabaseConnectivify)storeandanLDAP(LightweightDirectoryAccessProtocol)store.

TheRI(referenceimplementation)oftheJavaEESecurityAPIiscalledSoteria.ThisimplementationisofcourseprovidedbytheJavaEE8RIGlassFishanditsderivativePayara.Soteria,likeMojarraandMyFaces,hasbeendesignedtorunindependentlyonbasicallyeveryServletcontainerprovidedthatitadherestotheJASPICspecs(whichbothTomcatandJettydoasmentionedinthechapter's

Page 576: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

introduction).Itsdependencies(atthetimeofwriting,forSoteria1.0)areCDI1.2andExpressionLanguage3.0.ThesetwodependenciesareprovidedbyallJavaEE7implementations.ServletcontainerstypicallyprovideEL(ExpressionLanguage)supportwhileCDIcanbeaddedseparately,forinstance,usingtheRIWeld.ForafewoptionalfeaturesSoteriatakesadvantageofServletcontainersthatadheretotheJACCspec.

ProtectAccesstoResourcesInJSFthemainresourcestoprotectareviews,whichareaccessedviaURLpatterns.It'sthereforetheseURLpatternsweneedtodefinesecurityconstraints.Theseconstraintsareprimarilydefinedinweb.xml.Infact,asofJavaEE8,web.xmlisprettymuchtheonlyviableplacetodefinesecurityconstraintsforJSFviews.

JavaEEhasthreekindsofsecurityconstraints:Excluded

Unchecked

Byrole

EXCLUDED“Excluded,”aka“denyall,”meansthatnoexternalcallerwillbegrantedaccesstotheresourcescoveredbythisconstraint.Onemightaskwhatthepurposeofsuchaconstraintis.Iftheresourcescanneverbeaccessed,whyarethoseresourcesthereinthefirstplace?

Theansweristwofold.Forone,asingleapplicationmaybeconfiguredfordifferentpurposes.Anexcludedsecurity

Page 577: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

constraintisaneasywaythentoquicklydisableanumberofresourcesthatarenotapplicabletoacertainconfigurationoftheapplication.Amoreimportantusecaseistobeabletomakeadistinctionbetweenexternaluseandinternaluseofaresource.Thekeyinsighthereisthatsecurityconstraintsareonlyappliedtoexternalrequestsforthatresource,i.e.,toacallerrequestinghttps://example.com/resources/template.xht

ml,butnottointernalrequestssuchasincludes,forwards,andanyofthemethodstoloadaresourcefromtheclasspathorfilesystem.

ThisisspecificallyimportantforJSFbecauseofasomewhatunfortunatedesignchoiceregardingcompositecomponents.CompositecomponentsarecomponentsthatareimplementedviaaFaceletinsteadofaJavaclass.Byconvention,theyhavetobeplacedinadirectoryinsideadirectorynamed/resourcesthatresidesinthewebroot.Forinstance,/resources/bar/foo.xhtml.Thiswillmakeacompositecomponent“foo”availableinthenamespacehttp://xmlns.jcp.org/jsf/composite/bar.

Componentsareofcoursenotviewsandthecallershouldnotbeabletorequestthosedirectly.Unfortunately,/resourcesisnotinanywayaspecialdirectorytoJavaEE.JSFassignsaspecialmeaningtoitbyconvention,buttotheServletcontainerit’sadirectorylikeanyother.Thisspecificallymeansthere’snoprotectionappliedtoitandanycallercandirectlyrequestresourcesfromit.Inotherwords,thisdirectoryis“unchecked,”aka“worldreadable.”Evenwithan*.xhtmlmapping,thisnotonlyallowstheusertoguess

Page 578: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

whichcomponentswehavebutletstheuserattempttoexecutethoseaswell.Clearlythisisnotwhatwewant.Therearetwosolutionsforthis:

ConfigureanotherdirectorytobetheJSFresourcesdirectory

Addthementionedsecurityconstrainttoweb.xml

Viathejavax.faces.WEBAPP_RESOURCES_DIRECTORY

contextparameteranotherdirectorycanbeconfiguredtobetheJSFresourcesdirectoryinsteadof/resources.Forexample,<context-param><param-

name>javax.faces.WEBAPP_RESOURCES_DIRECTORY</param-name>

<param-value>WEB-INF/resources</param-value>

</context-param>

Notethatthepathisrelativetothewebrootandmustnotbeginwitha“/.”

Whilethisisagooddefaultforourownapplications,itstilldoesn’ttotallyprotectus.Namely,third-partyjarscanstillprovidetheirresourcesvia/resourcesandaren’taffectedbythatcontextparameter.Forthatreason,theaforementioned“Excluded”constraintisneeded.Itlooksasfollows:<security-constraint>

<web-resource-collection>

<web-resource-name>Theresourcesfolder<web-

resource-name>

<url-pattern>/resources/*</url-pattern>

</web-resource-collection>

</security-constraint>

Ascanbeseen,defininganexcludedconstraintboilsdownto

Page 579: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

definingtheURLpatternthatwewishtoconstrain,withoutdefininganyspecificconstraints.

UNCHECKED“Unchecked,”aka“permitall,”“public,”and“worldreadable,”meansthatallcallers,independentofwhetherornottheyareauthenticated,haveaccesstotheresourcescoveredbythe“constraint.”Internallyanexplicitconstraintmayexistforthis,butinweb.xmlthis“constraint”isdefinedsimplybynotdefininganyconstraintatallforaURLpattern.Inotherwords,everyURLthat’snotcoveredexplicitlybyanyotherpatternis“unchecked.”

BYROLE“Byrole”meansaso-calledroleisassociatedwithaURLpattern,andtheauthenticatedcallermusthavethatroleinordertoaccesstheresource.Aroleitselfisfrequentlyseenasaconceptwithstrictsemantics,butit’sessentiallylittlemorethanjustanopaquestringthatneedstomatchfromthesetofsuchstringsassociatedwithanauthenticatedcaller,andthesetofstringsassociatedwithaURLpattern.Thecontentofthatstringiscompletelyuptotheapplication,meaningitcouldbea“typeofcaller,”like“admin”,“user”,etc.,butalsosomethingfine-grainedas“may_add_item”,oreventokenssuchas“AYUDE-OPWR-BM1OP”.

Thefollowinggivesanexample:

<security-constraint>

<web-resource-collection>

<web-resource-name>Userpages</web-resource-name>

Page 580: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<url-pattern>user*</url-pattern>

</web-resource-collection>

<auth-constraint>

<role-name>VIEW_USER_PAGES</role-name>

</auth-constraint>

</security-constraint>

<security-constraint>

<web-resource-collection>

<web-resource-name>Adminpages</web-resource-name>

<url-pattern>admin*</url-pattern>

</web-resource-collection>

<auth-constraint>

<role-name>VIEW_ADMIN_PAGES</role-name>

</auth-constraint>

</security-constraint>

<security-role>

<role-name>VIEW_USER_PAGES</role-name>

</security-role>

<security-role>

<role-name>VIEW_ADMIN_PAGES</role-name>

</security-role>

Intheabovefragmentwedefinetwosecurityconstraints—onefortheuser*pattern,forwhichthecallerneedstohavetherole“VIEW_USER_PAGES”,andonefortheadmin*pattern,forwhichthecallerneedstherole“VIEW_ADMIN_PAGES”.Withtheaboveconstraintsinplace,acalleraccessing,say,useraccount.xhtmlhastobeauthenticatedandhastohavethementionedrole.

Anauth-constraintcancontainmultipleroles,forwhichORsemanticsareapplied.Thismeanstheauthenticatedcalleronlyneedstohaveoneoftherolesinthatconstraintinordertobegrantedaccess.ConstraintscanadditionallyberestrictedtoaspecificHTTPmethod(suchasGETorPOST).Thisdoes

Page 581: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

comewithacaveat,sincebydefaultallmethodsthatarenotspecifiedwillbeunchecked(public).Thiscanbecounteredbyusingthetop-level<deny-uncovered-http-methods/>tag.Thefollowinggivesanexampleofthis:<deny-uncovered-http-methods/>

<security-constraint>

<web-resource-collection>

<web-resource-name>Userpages</web-resource-

name>

<url-pattern>user*</url-pattern>

<http-method>GET</http-method>

<http-method>POST</http-method>

</web-resource-collection>

<auth-constraint>

<role-name>VIEW_USER_PAGES</role-name>

</auth-constraint>

</security-constraint>

SettingtheAuthenticationMechanismAfterthesecurityconstraintshavebeendefined,weneedtosethowthecallerwillauthenticate.Theartifactthathandlestheinteractionwiththecaller(i.e.,asksthecallerforcredentialsinacertainway)iscalledan“authenticationmechanism.”JavaEEprovidesanumberoftheseoutofthebox.TheServletspecprovidesfour,namely,FORM,BASIC,DIGEST,andCERT,whileJavaEESecurityprovidesFORMandBASICaswell(withthedifferencethatthesecorrespondtoCDIbeans),butalsoavariantofFORMcalled“CustomFORM”.

Page 582: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

FORMandCustomFORMarebothsuitableforinteractivewebapplications,suchastheoneswewouldbuildprimarilywithJSF.Contraryto,say,BASIC,theFORMauthenticationmechanismscallbackintotheapplicationandletitrendertheformthatasksthecallerforcredentials,hence,thenameofthesemechanisms.

Thedifferencebetweenthetwoismainlyinhowthemechanismrequirestheapplicationtocontinuetheso-calledauthenticationdialogafterithasrenderedtheform.InFORMthisisdonebylettingthecallerpostthefilled-outformtothevirtualURLj_security_check,whileinCustomFORMthisisdoneprogrammaticallyviaacalltotheinjectedSecurityContext.ThissmalldifferencemakesallthedifferenceforJSFthough.InJSF,aformviewbydefaultsubmitstothesameURLitwasrequestedfrom,sopostingbacktoasinglemechanismmandatedURLisnotatallnatural.Inadditiontothat,inJSFweoftenneedtohaveserver-sidecoderunningafterapostback;justthinkaboutconvertersandvalidatorsandtheabilitytoemitafacesmessage.Wecan’treallydoanyofthisifwehavetopostbacktothevirtualnon-facesj_security_checkURL,butit’squitedoablewhenwecancontinuetheauthenticationdialogprogrammaticallyfromthebackingbean’sactionmethod.

We’llfirstshowyouhowtoconfigureawebapplicationtousetheCustomFORMauthenticationmechanism.Lateron,we’llexplainhowtoactuallyuseJSFtofulfilltherequirementsthismechanismimposesontheapplication.

AlloftheauthenticationmechanismsprovidedbyJavaEESecurityareinstalledandconfiguredviaitsown

Page 583: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

AuthenticationMechanismDefinitionannotation.Thisannotationtellsthecontainerwhichtypeofauthenticationmechanismtoinstall,andwithwhichconfiguration.Theannotationcanbeplacedonprettymucheveryclassontheclasspath.Logicallyitfitsinquitewellwiththe@FacesConfigannotation.Thefollowingshowsanexample:

@CustomFormAuthenticationMechanismDefinition(

loginToContinue=@LoginToContinue(

loginPage="/login.xhtml",

errorPage=""

)

)

@FacesConfig@ApplicationScoped

publicclassApplicationConfig{

//...

}

AperhapssomewhatunfortunaterequirementhereisthattheerrorPageattributehastobespecifiedandsettotheemptystring.Thisisneededsincethe@LoginToContinueelementhasadefaultvalueforanactualerrorpage.InJSFwerarelyuseanexpliciterrorpagetodisplayerrorsbutinsteadredisplaytheoriginalpagewiththeerrormessagesrenderedonitviafacesmessages.

DonotethatthecurrentversionofJavaEESecurityonlyallowsoneauthenticationmechanismtobeactiveatthesametime.Technicallywhathappenswhenthecontainerencountersthe@CustomFormAuthenticationMechanismDefiniti

onannotationisthatitaddsanenabledCDIbeantothesystemoftypeHttpAuthenticationMechanism.This

Page 584: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

isimportanttoknow,sinceitsbeingaregularCDIbeanmeanswecaninjectit,decorateit,interceptit,andbasicallydoeverythingwithitthatwecannormallydowithCDIbeans.

TheloginToContinueattributeisusedtoconfiguretheviewthatthecontainerforwardstowheneverthecallertriestoaccessaprotectedview.Thisiscalled“containerinitiatedauthentication”;thecontainerstartstheauthenticationdialogasopposedtotheapplication.

Notethatthedefaultistoforwardtotheloginpage,meaningthatifthecallertriestoaccesshttps://example.com/foo.xhtml,and/foo.xhtmlisprotected,thecallerwillstillsee/foo.xhtmlintheaddressbarandnot/login.xhtml.However,thepostbackisto/login.xhtml,soafterenteringcredentialsthecallerwouldgettoseethisintheaddressbar.AlternativelywecanconfiguretheloginToContinueattributetousearedirectinstead.

@CustomFormAuthenticationMechanismDefinition(

loginToContinue=@LoginToContinue(

loginPage="/login.xhtml",

useForwardToLogin=false,

errorPage=""

)

)

@FacesConfig@ApplicationScoped

publicclassApplicationConfig{

//...

}

SettingtheIdentityStoreAfterhavingdeclaredthesecurityconstraintsandsettingthe

Page 585: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

mechanismthatwe’dliketousetoauthenticate,there’sonefinalpieceofthepuzzleremaining:settingtheartifactthatcontainsthecaller’sdata,suchascredentials,name,androles.InJavaEESecuritythisartifactiscalledanidentitystore.

JavaEEprovidestwooftheseoutofthebox;onetoconnecttoadatabaseandonetoconnecttoLDAP(LightweightDirectoryAccessProtocol).TheJavaEESecurityRI(Soteria)additionallyshipswithanembeddedidentitystore.Mostapplicationserversprovideadditionalonesoftheirown,whichareoftenconfiguredoutsidetheapplication(e.g.,viaanadminconsole,CLI,orXMLconfigurationfilethat’sstoredinsidetheserver).

SettingandconfiguringtheJavaEESecurity-providedidentitystoresandtheoneprovidedbytheRIhappensinasimilarfashionastheauthenticationmechanisms:viaanIdentityStoreAnnotation.Justliketheauthenticationmechanismversion,thiswillcausethecontainertoaddanenabledCDIbeantothesystem,thistimeoneimplementingtheIdentityStoreinterface.

Thefollowingshowsanexampletogetherwithourearlierdefinitionoftheauthenticationmechanism:

@CustomFormAuthenticationMechanismDefinition(

loginToContinue=@LoginToContinue(

loginPage="/login.xhtml",

useForwardToLogin=false,

errorPage=""

)

)

@EmbeddedIdentityStoreDefinition({

@Credentials(

callerName="[email protected]",

password="secret1",

Page 586: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

groups={"VIEW_USER_PAGES","VIEW_ADMIN_PAGES"}

),

@Credentials(

callerName="[email protected]",

password="secret2",

groups={"VIEW_USER_PAGES"})

)

})

@FacesConfig@ApplicationScoped

publicclassApplicationConfig{

//...

}

Theabovecausesanembedded(in-memory)storetobecreated,withtwocallers(users),thefirstonebeinginthegroups“VIEW_USER_PAGES”and“VIEW_ADMIN_PAGES”andthesecondoneonlyinthegroup“VIEW_USER_PAGES”.Theauthenticationmechanismwillusethisidentitystoretovalidatethatthecredentials(callernameandpassword)matchand,iftheydo,togetthecorrectgroupsfromtheidentitystore.

Theobservantreadermaynoticethattheterminologyhaschanged.Beforeweweretalkingabout“roles,”whileallofasuddenthishaschangedto“groups.”Isthisamistake?Well,notreally.Groupsandrolesaresubtlydifferent.Botharejustopaquestringstothecontainer,butgroupscanbeoptionallymappedtoroles.Wewon’telaborateonthisprocessfurtherhere,butsufficeittosaythatbydefault,JavaEEmandatesaso-called1:1group-to-rolemapping,whichsimplymeansgroupsandrolesarethesame.

Tobetterunderstandidentitystoreswe’llgivetwomoreexampleshere.Forthefirstexamplewe’lllookatoneoftheidentitystoresthat’sprovidedbytheJavaEESecurityAPI—

Page 587: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

thedatabaseidentitystore.Thisstoreisactivatedandconfiguredusingthe@DatabaseIdentityStoreDefinitionannotation.Thethreemostimportantattributestoconfigurearethedatasource(whichrepresentstheSQLdatabase),theSQLquerytoobtainthe(hashed!)passwordgivenacallername,andtheSQLquerytoidentifywhichgroupsacallerisingiventhecallername.Thefollowinggivesanexample:@CustomFormAuthenticationMechanismDefinition(

loginToContinue=@LoginToContinue(

loginPage="/login.xhtml",

errorPage=""

)

)

@DatabaseIdentityStoreDefinition(

dataSourceLookup="java:app/MyDataSource",

callerQuery="SELECTpasswordFROMcallerWHERE

name=?",

groupsQuery="SELECTnameFROMgroupsWHERE

caller_name=?"

)

@DataSourceDefinition(

name="java:app/MyDataSource",

className="org.h2.jdbcx.JdbcDataSource",

url="jdbc:h2:~/test;DB_CLOSE_ON_EXIT=FALSE"

)

@FacesConfig@ApplicationScoped

publicclassApplicationConfig{

//...

}

Page 588: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Inthisexample,adatasourceisdefinedfortheH2databaseusingtheorg.h2.jdbcx.JdbcDataSourcedriver.NaturallythiscanbedoneinasimilarwayforanyotherdatabasethathasaJDBCdriver.Alternatively,thedatasourcecanbedefinedexternallytotheapplication.Thecallerquerythatweusedis“selectpasswordfromcallerwherename=?”,whichmeansweassumeatablewithatleasttwocolumns—oneholdingthecallername,andtheotherthehashedpassword.Suchtablecouldbecreatedby,forexample,thefollowingSQLstatement:CREATETABLEcaller(nameVARCHAR(32)PRIMARYKEY,

passwordVARCHAR(255)

)

Thequerythatweusedforthegroupsis“selectnamefromgroupswherecaller_name=?”,whichassumesatablewithatleasttwocolumns—thecallername,andthegroupname,withonerowforeachgroupthecallerisin.Suchatablecouldbecreatedby,forexample,thefollowingSQLstatement:

CREATETABLEcaller_groups(

caller_nameVARCHAR(32),

nameVARCHAR(32)

)

Whenpopulatingthecallertable,itmustbenotedthatadefaulthashalgorithmisassumedforthepasswordcolumn,namely,PBKDF2WithHmacSHA256.Thisalgorithmcan(should)becustomizedbysettingthenumberofiterations,thekeysize,andthesaltsize.

Insteadofusinganidentitystorethat'sprovidedbythe

Page 589: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

JavaEESecurityAPIwealsohavetheoptionofprovidingourowncustomone.Acommonusecaseforthatisusingtheapplication'sownservicestoloadtheapplication-specificuserdata.

Thefollowingshowsanexampleofsuchanidentitystore:

@ApplicationScoped

publicclassUserServiceIdentityStoreimplements

IdentityStore{

@Inject

privateUserServiceuserService;

@Override

publicCredentialValidationResultvalidate(Credential

credential){

UsernamePasswordCredentiallogin=

(UserNamePasswordCredential)credential;

Stringemail=login.getCaller();

Stringpassword=login.getPasswordAsString();

Optional<User>optionalUser=

userService.findByEmailAndPassword(email,

password);

if(optionalUser.isPresent()){

Useruser=optionalUser.get();

returnnewCredentialValidationResult(

user.getEmail(),

user.getRolesAsStrings()

);

}

else{

returnCredentialValidationResult.INVALID_RESULT;

}

}

}

Page 590: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Thereisnospecificregistrationneeded;theabovegivenclasssimplyneedstobepresentintheapplication(beonitsclasspath).WithsuchcustomidentitystorepresenttheApplicationConfigclassthereforedoesn’tneedanyconfigfortheidentitystore.

@CustomFormAuthenticationMechanismDefinition(

loginToContinue=@LoginToContinue(

loginPage="/login.xhtml",

errorPage=""

)

)

@FacesConfig@ApplicationScoped

publicclassApplicationConfig{

//...

}

TheUserServiceIdentityStoreasgivenabovedelegatesmostoftheworktoaUserService,whichwouldberesponsibleforhandlingUserentitiesintheapplication.Fullydiscussingsuchserviceisoutsidethescopeofthisbook,butwecanimagineitcoulduse,forexample,JPAtopersistandloadUserentities.OurcustomidentitystoreusestheservicetotrytofindaUserbasedontheusernameandthepasswordthat'sbeingpassedinviathecredentials.IfaUserinstanceisreturned,itmeansthenamereferredtoanexistinguser,andthepasswordwasthecorrectone.InthatcasetheidentitystoreinturnreturnsCredentialValidationResultwhichdoestwothings:itindicatesthatauthenticationwassuccessful,anditprovidesthecontainerwiththedatathatwilleventuallybeusedtosettheauthenticatedidentityforthecurrentrequest.Iftheservicecouldn’tfindtheuser,theneitherthenameorthepassword

Page 591: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

waswrong.InthatcasethestorereturnsINVALID_RESULTtoindicatethatauthenticationwasnotsuccessful.

ProvidingOurCustomJSFCodeInthesectionsabove,wefirstdefinedoursecurityconstraints(whichviewsareprotected),thenwesetuptheauthenticationmechanism(howdoourcallersinteractwithourapplicationinordertoauthenticate),andfinallywesetuptheidentitystore(wherethecallerdataresides).

It’snowtimetoplugourowncustomcodeintotheauthenticationprocess.Thisprimarilyhappensbyprovidingtheviewthattheauthenticationmechanismdirectstowhenitneedstocollectthecaller’scredentials(e-mailandpasswordinthiscase).

Theloginpagecanbekeptrelativesimple—astandardformpage,withtwoinputsboundtoourbackingbean,andabuttontosubmit.

<!DOCTYPEhtml>

<htmllang="en"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

>

<h:head>

<title>LogIn</title>

</h:head>

<h:body>

<h1>LogIn</h1>

<h:form>

<h:outputLabelfor="email"value="Email"/>

<h:inputTextid="email"value="#{login.email}"/>

<br/>

<h:outputLabelfor="password"value="Password"/>

<h:inputSecretid="password"value="#

Page 592: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

{login.password}"/>

<br/>

<h:commandButtonvalue="Login"action="#

{login.submit}"/>

<h:messages/>

</h:form>

</h:body>

</html>

ThepageaboveisatotallynormalJSFview.ThisspecificallymeansthatcontrarytoclassicalHTML<formmethod="post"action="j_security_check">ofFORMauthenticationtheauthenticationmechanismdoesnotmonitorthepostbackURLinanywayandthereforetherearen’tanyconstraintsbeingplacedontheinputelementsthatcollectthecredentials.

Instead,theJSFbackingbeanhastocollectthesecredentialsandthenprogrammaticallypassthesealongandsignaltheauthenticationmechanismtocontinuethedialog.Beforepassingthecredentialsalong,JSFisfreetodoitsownvalidationandengageinitsowndialogwiththecallerwithouttheauthenticationmechanismhavingtobeinvolvedwiththis.Notethatthecommandbuttonexplicitlydoesn’tuseAjaxtosubmit.Youcandoso,butthentheaveragewebbrowserwon’tsuggesttheendusertorememberthelogincredentialsonitsbehalf.

Thefollowingshowsafullexampleofthebackingbeanhandlingthelogincall.We’lldiscussthepartsindividuallybelow.

@Named@RequestScoped

publicclassLogin{

Page 593: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@NotNull

@Email

privateStringemail;

@NotNull

@Size(min=8,message="Passwordmustbeatleast8

characters")

privateStringpassword;

@Inject

privateSecurityContextsecurityContext;

@Inject

privateExternalContextexternalContext;

@Inject

privateFacesContextfacesContext;

publicvoidsubmit(){

switch(continueAuthentication()){

caseSEND_CONTINUE:

facesContext.responseComplete();

break;

caseSEND_FAILURE:

facesContext.addMessage(null,new

FacesMessage(

FacesMessage.SEVERITY_ERROR,"Login

failed",null));

break;

caseSUCCESS:

facesContext.addMessage(null,new

FacesMessage(

FacesMessage.SEVERITY_INFO,"Login

succeed",null));

break;

caseNOT_DONE:

//Doesn’thappenhere

Page 594: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

}

privateAuthenticationStatuscontinueAuthentication(){

returnsecurityContext.authenticate(

(HttpServletRequest)

externalContext.getRequest(),

(HttpServletResponse)

externalContext.getResponse(),

AuthenticationParameters.withParams().credential(

newUsernamePasswordCredential(email,

password))

);

}

publicStringgetEmail(){

returnemail;

}

publicvoidsetEmail(Stringemail){

this.email=email;

}

publicStringgetPassword(){

returnpassword;

}

publicvoidsetPassword(Stringpassword){

this.password=password;

}

}

Westartthebackingbeanwithtwoinstancevariablescorrespondingwiththecredentialswecollect.

@NotNull

@Email

privateStringemail;

Page 595: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@NotNull

@Size(min=8,message="Passwordmustbeatleast8

characters")

privateStringpassword;

ThisclearlyshowstheadvantageovertheFORMauthenticationmechanisminthatwecaneasilypre-validatetheuser’sinputusingbeanvalidation.BecauseofJSF’sbuilt-inintegrationwithbeanvalidationastandardfacesmessagewillbemadeavailableforrenderingshouldtheinputnotpassvalidation.

Next,wedefinedinjectingthecontextualobjectsitneeds.Theseare@InjectprivateSecurityContextsecurityContext;

@Inject

privateExternalContextexternalContext;

@Inject

privateFacesContextfacesContext;

TheobservantreaderwillrecognizeExternalContextandFacesContextasbeingtwowell-knownnativeJSFclasses,withtheSecurityContextbeingtheoddoneout.ThisclassisfromJavaEESecurityandwe’llusethatheretocommunicatewiththeauthenticationmechanism.

ContinuingthedialoghappensinthecontinueAuthentication()methodasfollows:

privateAuthenticationStatuscontinueAuthentication(){

returnsecurityContext.authenticate(

(HttpServletRequest)externalContext.getRequest(),

(HttpServletResponse)externalContext.getResponse(),

Page 596: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

AuthenticationParameters.withParams().credential(

newUsernamePasswordCredential(email,password))

);

}

ThecalltoSecurityContext#authenticate()willtriggertheauthenticationmechanismagain.Sincethatmechanismwillbeastatewhereitwaitsforcredentialstobepassed,itwillindeedlookforthecredentialswepassin,andusethosetocontinue.Aswe’lllatersee,wecanalsorequestthatanypotentiallyexistingstateisdiscardedandanewdialogisstarted.NotethatwehavetocasttherequestandresponseobjectstoHttpServletRequestandHttpServletResponse.Unfortunately,thisisneededsinceExternalContextabstractsoverServletandPortletrequestsandonlyreturnsObjectforthosetwo.

TheSecurityContext#authenticate()methodreturnsastatusthatindicatesinbroadlineswhattheauthenticationmechanismdid.TheactionmethodofourJSFbackingbeanhastohandlethefollowing:

switch(continueAuthentication()){

caseSEND_CONTINUE:

facesContext.responseComplete();

break;

caseSEND_FAILURE:

facesContext.addMessage(null,newFacesMessage(

FacesMessage.SEVERITY_ERROR,"Loginfailed",

null));

break;

caseSUCCESS:

facesContext.addMessage(null,newFacesMessage(

FacesMessage.SEVERITY_INFO,"Loginsucceed",

Page 597: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

null));

break;

caseNOT_DONE:

//Doesn'thappenhere

}

Ascanbeseen,therearefourpossibleoutcomes.ThefirstoneisSEND_CONTINUE,whichbasically

means“authenticationinprogress.”Theauthenticationmechanismreturnedthatstatuswhenittookoverthedialogagain(e.g.,byrenderingitsownresponseor,morelikely,byredirectingthecallertoanewlocation).AJSFbackingbeanshouldmakesuretheJSFlifecycleisendedbycallingFacesContext#responseComplete()andfurthermorerefrainfrominteractingwiththeresponseitselfinanyway.

ThesecondoneisSEND_FAILURE,whichbasicallymeans“authenticationfailed.”Thisstatusisreturnedwhentheauthenticationmechanismwasn’tabletovalidatethecredentialsthatwereprovided.Inmostcasesthisiswhenthecallerprovidedthewrongcredentials.AJSFbackingbeancanrespondtothisbysettingafacesmessageandredisplaytheloginform.

ThethirdstatusisSUCCESS,whichmeans“authenticationsucceeded.”Thisisreturnedwhentheauthenticationmechanismsuccessfullyvalidatedthecredentialsprovided.It’sonlyafterthisstatusisreturnedthatHttpServletRequest#getUserPrincipal(),SecurityContext#getCallerPrincipal(),etc.,returnnon-nullvaluestoindicatethecurrentcallerisauthenticated.AJSFbackingbeancanrespondtothisin

Page 598: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

variousways(e.g.,bysettingafacesmessageandcontinuingtorendertheview,orissuingaredirectofitself).

ThefourthandfinalstatusisNOT_DONE,whichisreturnedwhentheauthenticationmechanismchoosestonotauthenticateatall.Thishappens,forinstance,whentheauthenticationmechanismispre-emptivelycalledbutauthenticationappearednottobenecessary.Typically,aJSFbackingbeanwouldnotneedtotakeanyspecialactionhere.

Caller-InitiatedAuthenticationThepreviouscodediscussedthesituationwhereanunauthenticatedcallertriestoaccessaprotectedresource(URL/page)andtheauthenticationdialogisautomaticallystarted.Sincethisauthenticationdialogisstartedbythecontainer,wecallthis“container-initiatedauthentication.”

Anothercaseiswhereacallerexplicitlystartstheauthenticationdialog(e.g.,byclickingona“login”button).Becausethecallerstartsthisdialogwecallit“caller-initiatedauthentication.”

Incaseofcaller-initiatedauthentication,thecoreauthenticationmechanismiseffectivelydirectlyinvokedandtheplatform-providedlogin-to-continuefunctionalityisskipped.Thismeansthatifanauthenticationmechanismdependsonlogin-to-continuetoredirecttoaloginpageandafterauthenticationtoredirectbacktotheprotectedresource,neitherofthesetwoactionswillhappenwhentheapplicationprogrammaticallytriggersauthentication.

TheCustomFormAuthenticationMechanismthatwedefinedearlierviaanannotationisindeedamechanismthatusestheplatform’slogin-to-continueservice,sowe’ll

Page 599: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

starttheauthenticationdialogbydirectingtothesameloginviewweusedbefore.Toindicatethisisanewlogin,anextrarequestparameterisprovided.Theviewfromwhichwestartlooksasfollows:<!DOCTYPEhtml><htmllang="en"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:f="http://xmlns.jcp.org/jsf/core"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"

>

<h:head>

<title>Welcome</title>

<h:head>

<h:body>

<c:iftest="#{notemptyrequest.userPrincipal}">

<p>Logged-inas#{request.userPrincipal}</p>

</c:if>

<c:iftest="#{emptyrequest.userPrincipal}">

<h:form>

<h:buttonvalue="Login"

outcome="/login">

<f:paramname="new"value="true"/>

</h:button>

</h:form>

</c:if>

</h:body>

</html>

Inthebackingbeanwe’llinjecttwoadditionalobjects:aninstancetoobtainandstorethementionedrequestparameterandareferencetotheFlash,whichwe’lluselater.

Page 600: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Inject

privateFlashflash;

@Inject@ManagedProperty("#{param.new}")

privatebooleanisNew;

Themanagedbean’sscopeneedstobechangedto@ViewScoped,sowecanretainthevalueoftheisNewinstancevariableaftertheloginform’spostback.

AnimportantadditionistotheSecurityContext#authenticate()methodwherewe’llnowprovideanextraparameter:newAuthentication.Theauthenticationmechanismdoesnotstrictlyneedthisthough,andit’sjustsmartenoughtodistinguishbetweenaninitialnewauthenticationandcontinuinganauthenticationdialogthat’sinprogress.However,thingsgetmoredifficultwhenacallerisinthemidstofanauthenticationdialogandthennavigatesaway,onlytoexplicitlyclickaloginbuttonlater.Ifthestateassociatedwithsaiddialoghasn’texpiredatthatpoint,theauthenticationmechanismdoesn’tknowanewauthenticationisrequiredandwilllikelycontinuetheabortedbutstillvaliddialog.

Topreventthis,wecanforceanewauthenticationbysettingnewAuthenticationtotrue.Thiswilldiscardallexistingstates.ThemodifiedcontinueAuthentication()methodlooksasfollows:

privateAuthenticationStatuscontinueAuthentication(){

returnsecurityContext.authenticate(

(HttpServletRequest)externalContext.getRequest(),

(HttpServletResponse)externalContext.getResponse(),

Page 601: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

AuthenticationParameters.withParams()

.newAuthentication(isNew).credential(

newUsernamePasswordCredential(email,

password))

);

}

Notethatthisversioncanbeusedforthecasewherewecontinuethedialogaswellastostartanewone.Whenwecontinuethedialog,isNewwillsimplybefalse,whichalsohappenstobethedefaultwhentheparameterisnotspecifiedatall.

WhenusingtheCustomFormAuthenticationMechanismweknowtherewillnotbeanyredirectsorotherwritestotheresponseafterweprovidethecredentialsincaller-initiatedauthentication,sothatgivesusaconvenientlocationtohandletheredirecttoalandingpageafterthecallerauthenticates:theSUCCESScase.

caseSUCCESS:

flash.setKeepMessages(true);

facesContext.addMessage(null,newFacesMessage(

FacesMessage.SEVERITY_INFO,"Loginsucceed",null));

externalContext.redirect(

externalContext.getRequestContextPath()+

"/index.xhtml");

break;

We’reredirectingthecallerheretotheindex.xhtmllandingpage.Notethatthisisalsotheviewwherethecallerinitiatedtheauthenticationdialog,butthat’sjustacoincidenceinthisexample.Ingeneral,thevieworevenURLwhereweredirectthecallertoiscompletelyfreefortheapplication

Page 602: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

developertochoose.Typically,alandingpageofsomesortischosen,whichcouldbetheindexoftheapplicationoradashboardcorrespondingtothemainrolethecallerisin.Aswementionedabove,whenSUCCESSisreturnedthecallerisfullyauthenticated.Thismeanswecanquerythecaller’srolesandusetheseinourdecisionwheretoredirectto.

Outlook:afutureversionofJavaEESecuritymayintroduceahybridoptionwherecaller-initiatedauthenticationcanstillstartwiththesameredirectascontainer-initiatedauthenticationandallowsfortheredirect-backURLtobeprovidedbytheapplication.

RememberMeOnceacallerhasbeenauthenticatedforaJSF(web)application,wenaturallydon’twanttoaskthecallertore-authenticatewitheveryrequest.Topreventthis,theresultofasuccessfulauthenticationistypicallycoupledinsomewaytothecaller’sHTTPsession.Infact,theCustomFormAuthenticationMechanisminternallyusestheJavaEESecurity’sprovided“auto-apply-session”servicetodojustthis.Thisservicestoresthedataassociatedwithsaidsuccessfulauthentication(atleastthecallernameplusanygroupsthecallerisin).Althoughimplementationultimatelydependsonwherethisdataexactlylivesandwithwhatlifetime,inpracticeit’stypicallyinaspecialsectionoftheserver’smemoryassociatedwiththeHTTPsession.Thissectionisspecialinthewaythatit’stypicallysessionscoped,butthedataisnotaccessibleviaHttpSession#getAttribute().

Inordertonotexhausttheserver’smemory,anHTTP

Page 603: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

sessionexpiresafteracertainamountoftime.Typicalexpirationtimesarebetween10minutesandanhour.Ifthecalleraccessestheapplicationafterthistime,authenticationisrequiredtobeperformedagain.

Oftenthoughevenre-authenticatingafteraperiodofinactivityaslongasanhourisundesirable.ButextendingtheHTTPsessiontoalongerperiodisundoablefortheaforementionedreasonsofserverresourceexhaustion.

Here’swhere“RememberMe”(remember-me)comesin.Remember-meisasomewhatplayfultermforaprocesswherethecaller’scredentialsareexchangedforatoken,andwherethistokenistypicallystoredinacookiethat’sdistinctfromtheHTTPsessioncookieandhasalongertimetolive.

Aremember-metokeneffectivelyfunctionsasanewcredentialforthecaller,withoutexposingthecaller’soriginalpassword.Aremember-metokencanbasicallybevendedmultipletimes,forinstance,onceperdeviceorIP(Internetprotocol)thatthecallerusestoconnecttotheapplication.Caremustbetakenthatwhilethetokendoesnotexposethecaller’soriginalcredentials,itstillfunctionsasthekeytoacaller’saccountandthereforeshouldbetreatedwiththesameprecautionsasonewouldapplytoanyothertypeofcredential.Specifically,cookiescontainingtheremember-metokenshouldbesentoverHTTPS/SSLonly,andapplicationsshouldnotstoretheactualtokenverbatimbutastronghashofit.

Astheprimaryreasonforhavingremember-meistonotexhaustservermemoryandtobelong-lived,theremember-metokenisalmostalwaysstoredinstablestorage(e.g.,adatabase).Assuch,alookupfromsuchstorageiscostlierthanalookupfromtheserver’smemory,andthiscouldseriouslyaffectperformancewhenrequiredtobedoneforevery

Page 604: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

request,especiallywhenmanyAjaxrequestsarebeingdoneallthetime.

Forthisreason,remember-meisalmostalwaysusedincombinationwithsomekindofcache.ThemodularnatureoftheservicesthattheCustomFormAuthenticationMechanismusesmakesitpossibleforremember-metobeinsertedbetweentheauto-apply-sessionservicementionedaboveandtheactualauthenticationmechanism.Thatwayweeffectivelygetakindofmemoryhierarchy;theauthenticationdataisfirstattemptedtobefoundintheHTTPsessionstorage,ifit’snottheretheremember-meserviceisattempted,andifthatonedoesn’tcontainthedatathenfinallytheauthenticationmechanismistried.

Tomakeuseofremember-me,twothingshavetobedone.1. Activatingtheremember-meservicefortheinstalledauthentication

mechanism

2. Providingaspecialidentitystorethat’scapableofvendingandvalidatingtheremember-metoken

ACTIVATINGREMEMBERMESERVICETheremember-meserviceinJavaEESecurityisrepresentedbyanInterceptor.Viatheinterceptorbindingannotation@RememberMe[theremember-meservice]iseasilyappliedtoourowncustomauthenticationmechanism,oneforwhichwehavethesourcecode.Unfortunately,itisn’taseasywhenthesehavetobeappliedtoabeanforwhichwedon’thavethesourcecodeandinfactforwhichwedon’tevenknowtheexactimplementationtype.

Page 605: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

AstheCustomFormAuthenticationMechanismthatwe’vebeenusingfortheexamplesaboveisindeedofthelattertype,there’sabitmoreworktodo.Essentially,weneedtoobtainareferenceoftheactualCustomFormAuthenticationMechanism

implementationthatthecontainermakesavailableandthenusetheCDI2.0InterceptionFactorytoprogrammaticallyaddthe@RememberMeannotation.Theresultisthentobereturnedfromanalternativeproducermethod.

ThisisdemonstratedinthefollowingcodeviathenewmethodproduceAuthenticationMechanism()intheApplicationConfigbeanwhichweshowedbefore:@CustomFormAuthenticationMechanismDefinition(

loginToContinue=@LoginToContinue(

loginPage="/login.xhtml",

useForwardToLogin=false,

errorPage=""

)

)

@FacesConfig@ApplicationScoped

@Alternative@Priority(500)

publicclassApplicationConfig{

@Produces

publicHttpAuthenticationMechanism

produceAuthenticationMechanism(

InterceptionFactory<HttpAuthenticationMechanismW

rapper>

interceptionFactory,BeanManagerbeanManager

Page 606: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

){

@RememberMe

classRememberMeClass{};

interceptionFactory.configure().add(

RememberMeClass.class.getAnnotation(Remember

Me.class));

return

interceptionFactory.createInterceptedInstance(

newHttpAuthenticationMechanismWrapper(

(HttpAuthenticationMechanism)

beanManager

.getReference(beanManager

.resolve(beanManager

.getBeans((HttpAuthenticationMechanism.c

lass).stream()

.filter(b->b.getBeanClass()!=

ApplicationConfig.class)

.collect(Collectors.toSet())),

HttpAuthenticationMechanism.class,

beanManager.createCreationalContext(

null))));

}

}

TheApplicationConfigbeanisannotatedwiththe@Alternativeand@Priorityannotations.@Alternativeisusedheretoindicatethattheproducerisnotjustanyregularproducerbutonethatshouldbecalledinsteadofanyexistingproducerorbean.Thatis,thebeanweareproducinghereisanalternativeforthebeanwiththesametypethatwouldotherwisebeselectedbyCDIforinjection.

Page 607: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Priorityisusedtoenable(activate)ouralternativeproducer.Withoutthisannotationtheproducerispresentbutnotenabled,meaningthatCDIwon’tcallit.Anotherwayofenablinganalternativeisusingbeans.xml.Thenumber500hereisusedtoselectbetweenvariousalternativesifmultiplealternativesareenabled.Inthatcasetheonewiththehighestnumberisselected.

Thecodeshownaboveusesthesomewhatwell-knownCDIpatternBeanManager#getBeans()/resolve()/getRefere

nce()toobtaintheCustomFormAuthenticationMechanismthatthecontainermakesavailable.ThispatternismoreverbosethanthesimplerCDI.current().select(...)variant,butitallowsustofilterouttheBean<T>thatrepresentstheproducermethod.GettingareferencefromthatBean<T>fromwithintheproducermethodwouldinvokethatsameproducermethodagain,andthuswouldcausearecursiveseriesofcallseventuallyleadingtoastackoverflow.Itgoeswithoutsayingthisisunwanted,hencethereasonwefilterthatparticularBean<T>out.

ThebeaninstancethatisreturnedfromtheBeanManager#getReference()isalmostcertainlyaproxy;CustomFormAuthenticationMechanismisspecifiedtobeapplicationscoped,anditimplicitlymakesuseofaninterceptor.Duetothetechnicaldifficultyofproxyinganexistingproxy(thinkofgeneratedproxiesoftenbeingfinalandproxycachesbeingused)CDI2.0imposesalimitationonwhattypesofobjectsitcancreateaninterceptedinstancefrom.Toworkaroundthislimitation,wehavelittlechoicebut

Page 608: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

toinsertanextramanuallycreated“pass-throughwrapper”HttpAuthenticationMechanismWrapperinstanceasshowninthecodeabove.Thecodeofthiswrapperisasfollows:publicclassHttpAuthenticationMechanismWrapperimplementsHttpAuthenticationMechanism

{

privateHttpAuthenticationMechanismwrapped;

publicHttpAuthenticationMechanismWrapper(){

//

}

publicHttpAuthenticationMechanismWrapper

(HttpAuthenticationMechanism

httpAuthenticationMechanism)

{

this.wrapped=httpAuthenticationMechanism;

}

publicHttpAuthenticationMechanismgetWrapped(){

returnwrapped;

}

@Override

publicAuthenticationStatusvalidateRequest(

HttpServletRequestrequest,

HttpServletResponseresponse,

HttpMessageContextcontext)throws

AuthenticationException

{

Page 609: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

returngetWrapped().validateRequest(request,

response,context);

}

@Override

publicAuthenticationStatussecureResponse(

HttpServletRequestrequest,

HttpServletResponseresponse,

HttpMessageContextcontext)throws

AuthenticationException

{

returngetWrapped().secureResponse(request,

response,context);

}

@Override

publicvoidcleanSubject(

HttpServletRequestrequest,

HttpServletResponseresponse,

HttpMessageContextcontext)

{

getWrapped().cleanSubject(request,response,

context);

}

}

Outlook:it’sexpectedthataconveniencemethodfortheabovetaskwillbeaddedtoafutureversionofJavaEESecurity,therebygreatlysimplifyingthistask.

LoggingOut

Page 610: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Regardlessofwhichmethodtologinhasbeenused,atsomepointthecallermaywishtoexplicitlylogout.Anormallogin(authentication)inJavaEEisalwaysprimarilyvalidperrequestonly,butvariousauthenticationmechanismsortheservicesthey’reusing(suchas@AutoApplySessionand@RememberMe)maykeepthestatebeyondasinglerequestandautomaticallyre-authenticatethecallerateverynextrequest.

Thisstatemaybekeptatvariousplaces:incookies,intheHTTPsession,inclientstorage,etc.Inordertologoutwehavetomakesureallthisstateiscleared.InJSFwecandothissimplybycallingtheHttpServletRequest#logout()method.ThiswillimmediatelyremovetheauthenticatedidentityfromthecurrentrequestandcallthecleanSubject()methodoftheauthenticationmechanism,whichinturnwillremoveanysessiondata,cookies,etc.,thatitused.

Thefollowinggivesanexample:

@Named@RequestScoped

publicclassLogout{

@Inject

privateHttpServletRequestrequest;

publicvoidsubmit()throwsServletException{

request.logout();

request.getSession().invalidate();

}

}

Notethatforafulllogoutit'stypicallygoodpracticetoinvalidatethesessionaswell.Thecallto

Page 611: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

HttpServletRequest#logout()shouldonlyremovethesessionstateusedbytheauthenticationmechanism(ifany),whileafterafulllogoutweoftendon’twantanyothersessionstatelingeringaroundeither.Dependingontheapplicationdesignit'stypicaltoredirectthecallertothehomepageoftheapplicationafteralogoutaswell.

CustomPrincipalsThedefaultprincipalthatwecanobtainfromthesecuritycontextcontainsverylittleotherthanjustthenameor,moreexactly,thecallerprincipalname(alsoknownastheuserprincipalname).Thisistypicallyauniquenameandoften,butnotnecessarily,thenamethecallerusedtoauthenticatewith.

Inpractice,awebapplicationalmostalwaysneedsmoreinformationthanjustthisname,andaricherapplication-specificmodelobjectrepresentingtheuserisoftendesired.Thelifetimeofthismodelobjectdoesneedtobeverytightlycoupledtothatoftheprincipal.Forexample,ifthecallerisloggedoutmid-request,theassociatedmodelobjectmustdisappearrightaway,andifthecallerisloggedinagainrightafter(possiblystillinthesamerequest)anewmodelobjectmustbecomeavailable.

Therearevariouspatternstorealizethis,someofthemincludingServletfiltersandotherscontainingCDIproducers.Thepatternwe'regoingtoshowhere,though,involvesacustomprincipal.

AcustomprincipalmeansthataspecificPrincipaltypeisreturnedfromtheidentitystore,insteadofjustprovidingaStringandlettingthecontainerdecidethetype.ThisspecificPrincipaltypecantheneithercontainour

Page 612: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

modelobject(aggregation)orbethemodelobject(inheritance).We'llgiveanexampleoftheaggregationapproachhere.

FirstconsiderthefollowingcustomPrincipal:publicclassUserPrincipalextendsCallerPrincipal{

privatefinalUseruser;

publicUserPrincipal(Useruser){

super(user.getEmail());

this.user=user;

}

publicUsergetUser(){

returnuser;

}

}

Thisprincipalextendsfromjavax.security.enterprise.CallerPrincipal

whichistheJavaEESecurityAPI-specificcallerprincipalrepresentation.

WiththisPrincipalimplementationwecannowadjusttheidentitystorethatwepresentedearliertoreturnourcustomprincipalinstead.

@ApplicationScoped

publicclassUserServiceIdentityStoreimplements

IdentityStore{

@Inject

privateUserServiceuserService;

Page 613: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Override

publicCredentialValidationResultvalidate(Credential

credential){

UsernamePasswordCredentiallogin=

(UserNamePasswordCredential)credential;

Stringemail=login.getCaller();

Stringpassword=login.getPasswordAsString();

Optional<User>optionalUser=

userService.findByEmailAndPassword(email,

password);

if(optionalUser.isPresent()){

Useruser=optionalUser.get();

returnnewCredentialValidationResult(

newUserPrincipal(user),//Principalinstead

ofString.

user.getRolesAsStrings()

);

}

else{

returnCredentialValidationResult.INVALID_RESULT;

}

}

}

Subsequently,wecanaccessourmodelobjectagainfromaninjectedsecuritycontext.

@Inject

privateSecurityContextsecurityContext;

[...]

Optional<User>OptionalUser=

securityContext.getPrincipalsByType(UserPrincipal

.class)

.stream()

.map(e->e.getUser())

.findAny();

Page 614: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ConditionallyRenderingBasedonAccessInwebapplicationsoneoftenwantstorenderpartsofaviewdifferentlybasedonwhetheracallerisauthenticatedornot,andifsobasedonwhatrolesthiscallerisin.

JSFcomponenttagsdon’treallyneedspecialattributesforthis,astheexistingimplicitobjectscombinedwithexpressionlanguagearepowerfulenoughtodomostofthechecksneededforthis.

Oneofthemostcommonchecksisdeterminingwhethertheuserisauthenticated.Thiswasbrieflyshownintheindex.xhtmlviewabove:<c:iftest="#{notemptyrequest.userPrincipal}">

<p>Logged-inas#{request.userPrincipal}</p>

</c:if>

Youcan,ofcourse,alsousetherenderedattributeofanyJSFcomponenthere.

<ui:fragmentrendered="#{notemptyrequest.userPrincipal}">

<p>Logged-inas#{request.userPrincipal}</p>

</ui:fragment>

However,asyoulearnedinthesection“JSTLCoreTags”inChapter3,thiswillonlyendupinaslightlymoreverbosecomponenttree.Moreover,therenderedattributecheckswillbedonethroughouttheJSFlifecycleoverandoverwhileJSTLtagsareexecutedonlyonceduringviewbuildtime.

Notethatwe’reusingtheimplicitobject#{request}hereinsteadofthemoregeneralSecurityContext.ThisisbecauseinJavaEE8there’snoimplicitELobjectavailable

Page 615: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

correspondingtothisSecurityContext.InJavaEESecurity,aswellasintheServletAPI(fromwhichtherequest,whichisoftypeHttpServletRequestoriginates)it’sdefinedthatanullreturnfromgetUserPrincipal()meanstheuserisnotauthenticated.AbetteralignmentbetweenJavaEESecurityandExpressionLanguageisplannedforafutureversionofJavaEE.

Anothercommoncheckasmentionedistotestforthecallerbeinginaspecificrole.Heretoowecanusetheimplicitobject#{request},asshowninthefollowing:<c:iftest="#{request.isUserInRole('foo')}">

<!--foospecificthingshere-->

</c:if>

It’sgoodtorememberthatasexplainedinthebeginningofthischapter,therole“foo”doesn’thavetobesomethingthatwewouldcallaroleinournormalusageoftheword.Thatits,itdoesn’thavetobesomethinglike“admin”,or“manager”.Infact,forsuchverylocalusageasinafragmentonaviewit’softenpreferredtouseafiner-grainedname(e.g.,“CAN_UPDATE_SALARY”).Acommontechniqueistomapfine-grainedrolestomorecoarse-grainedroles,suchas,indeed,“ADMIN”.Viathistechniqueauserisgiventhesemorecoarse-grainedroles,andthedatastorethatstorestheauthenticationdatathenonlycontainsthesecoarse-grainedrolesaswell.Whenanidentitystoresuchaswesawaboveretrievesthisauthenticationdataforacertaincallerandsees“ADMIN”itwouldreturnacollectionofrolestowhich“ADMIN”ismapped(e.g.,{"CAN_UPDATE_SALARY","CAN_ADJUST_MARGINS",...}).

Aspecialrolethatwecantestforisthe“**”rolewhichis

Page 616: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

analternativeforthe#{notemptyrequest.userPrincipal}check.Thisroleisimplicitlyassignedtoanyauthenticatedcaller,butwiththecaveatthattheapplicationhasnotdeclaredthisinanyway.Ifithasdoneso,“**”losesitsspecialmeaningandisjustanotheropaquestringforwhichthesecuritysystemexplicitlytests.Usingthe“**”check,thefirstfragmentthatweshowedinthissectionlooksasfollows:<c:iftest="#{request.isUserInRole('**')}">

<p>Logged-inas#{request.userPrincipal}</p>

</c:if>

InthestandardJavaEEprogrammaticAPIstherearenomethodsavailabletotestwhetherthecallerisinanyoftwoormoreroles,orinalloftwoormoreroles.Ifthisisrequired,utilitymethodssuchasshowninthefollowingcodecanbeused:

publicstaticbooleanisInAnyRole(HttpServletRequestrequest,

String...roles){

for(Stringroles:roles){

if(request.isUserInRole(role)){

returntrue;

}

}

returnfalse;

}

publicstaticbooleanisInAllRoles(HttpServletRequest

request,String...roles){

for(Stringroles:roles){

if(!request.isUserInRole(role)){

returnfalse;

}

}

returntrue;

Page 617: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

Sometimesit’snecessarynotonlytorendercontentonaviewdifferently,dependingonwhatrolesacallerisin,butalsototakeintoaccountwhatotherviews(webresources)acallerisallowedtoaccess.Thiscomesintoplay,forinstance,whenrenderingnavigationmenus(omittingtheentriesforviewsacallerdoesnothaveaccessto),orrenderinglinksorbuttonsthatnavigatetoviewstowhichthecallerdoesnothaveaccessinaspecialway(e.g.,inredorwithalockiconnexttoit).

Atraditionalwaytoimplementthisistotestfortherolesthattheprogrammerknowsgiveaccesstothegivenview.Whilethismayseemtoworkwell,it’softenbrittleinpracticeasitletsthecodeworkundertheassumptionofaspecificrole/viewrelationshipwithoutanystrongguaranteesthatthisrelationshipactuallyholds.

Amorestablewaytotestwhetheracallerhasaccesstoagivenviewisquitesimplytotestdirectlyforexactlythat;doesthecallerhaveaccesstothisview(webresource).TheSecurityContexthasamethodthatcanbeusedforalmostexactlythis:SecurityContext#hasAccessToWebResource().SincetheSecurityContextisnotanamedbeanorimplicitobject,wehavetocreateasmallhelperbeaninordertousethisinEL.Thisisshownasfollows:@Named@ApplicationScoped

publicclassSecurity{

@Inject

privateSecurityContextsecurityContext;

Page 618: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicbooleanhasAccessToWebResource(String

resource){

return

securityContext.hasAccessToWebResource(resource,"GET");

}

}

Therearetwothingstobeawareofhere.First,thehasAccessToWebResource()method

takesawebresourcepattern,whichisthesamepatternasusedfortheurl-patternintheweb.xmlfragmentwelookedatearlier.Thisiscloseto,butnotexactlythesameas,theJSFview.TheJSFviewisoftenspecifiedinamappingindependentway(e.g.,/fooinsteadoffacesfooor/foo.xhtml).Thewebresourcepattern,however,hastobetheURLitself,withthemappingincluded.

Second,hasAccessToWebResource()requiresustospecifytheHTTPmethodforwhichwetesttheaccess.ThisisrequiredsinceinJavaEESecurityconstraintsactuallyapplyperURLandperHTTPmethod.Forinstance,acallercanhaveaccesstoPOSTto/foo.xhtmlbutnottoGET/foo.xhtml.Aswe’regoingtouseourutilitymethodfornavigationtests,GETistypicallytherightHTTPmethodtouse,butweshouldbeawarethatsometimeswemayneedtotestforanotherHTTPmethod.

Withthehelperbeaninplace,wecannoweasilycheckforaccesstoatargetresourceonaviewandaltertherenderingbasedonthat.Todemonstratethis,we’llfirstdefinethreenewwebresourceconstraintsinweb.xml.

<security-constraint>

Page 619: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<web-resource-collection>

<web-resource-name>Bar</web-resource-name>

<url-pattern>/bar.xhtml</url-pattern>

</web-resource-collection>

<auth-constraint>

<role-name>bar</role-name>

</auth-constraint>

</security-constraint>

<security-constraint>

<web-resource-collection>

<web-resource-name>Foo</web-resource-name>

<url-pattern>/foo.xhtml</url-pattern>

</web-resource-collection>

<auth-constraint>

<role-name>foo</role-name>

</auth-constraint>

</security-constraint>

<security-constraint>

<web-resource-collection>

<web-resource-name>Baz</web-resource-name>

<url-pattern>/baz.xhtml</url-pattern>

</web-resource-collection>

<auth-constraint>

<role-name>baz</role-name>

</auth-constraint>

</security-constraint>

Aftertheseconstraintshavebeendefinedwecanrenderlinkstothemwithaccesschecksontheenabledattribute.

<h:linkvalue="GotoBar"outcome="/bar"

disabled="#{not

security.hasAccessToWebResource('/bar.xhtml')}"/>

<h:linkvalue="GotoFoo"outcome="/foo"

disabled="#{not

security.hasAccessToWebResource('foo.xhtml')}">

<h:linkvalue="GotoBaz"outcome="/baz"

disabled="#{not

security.hasAccessToWebResource('/baz.xhtml')}"/>

Page 620: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Authenticatingwithacallerhaving,forinstance,theroles“bar”and“foo”,butnot“baz”,willresultinthelinkto/bazbeingrenderedasdisabled.

Cross-SiteRequestForgeryProtectionCross-siterequestforgery(CSRF)isanattackthatletsuserswithouttheirconsentorevenknowledgedoarequesttoasitewheretheymaypossiblybeloggedin.Suchrequestthenhassomesideeffectthatinsomeparticularwaymaybebeneficialtotheattacker.

Forinstance,supposeyourbankhasaURLoftheformhttps:/example.com/transferAmount=4000&tar

getAccount=7836whichmeans“transfer4000eurosfrommyaccounttotheaccountwithID7836.”Inthisstatementthe“myaccount”isbeingdeterminedviathelogged-insession(typicallyacookie)thatyouhavewithyourbank.Nowanattackermightnotbeabletocaptureyoursessioncookie,butthat’snotnecessaryinthisexampleifonlyyourbrowsercanbetrickedintosendingthehttps:/example.com/transferAmount=4000&tar

getAccount=7836requestfromanyotherwebsitethatyouvisitwhileyou’reloggedintoyourbankinanothertaborwindow,withthetargetAccountparametersettoanIDofanaccountthattheattackercontrols.NotethatinpracticeaGETrequestandrequestparameterswouldnotlikelybeused,butPOSTandPOSTparameterswouldbeusedinstead.However,forboththesamebasicvulnerabilityholds.

Ifwewanttoprotectourapplicationagainstreceivingsuchmaliciousrequests,thenoneofthewaystodosoisincluding

Page 621: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

“something”(i.e.,atoken)intherequestthatis1. Specifictoacertaincaller.

2. Expiresaftersometime.

3. Can’tbeeasilyguessed.

AsithappensJSFalreadyhassomethingthatfulfillsallthesethreerequirements,andthat’sthejavax.faces.ViewStatehiddenparameterthatonefindswithinJSFforms.

Caution

There’sacaveatthough,andthat’sthatthisparameteronlyfullyfulfillsthoserequirementswhenusingpostbacks,statesavingonserverisused,andtheviewinquestionisnotstateless.Onlyinthatcaseisthevalueofjavax.faces.ViewStateeffectivelyatoken.Sincethisisthedefault,JSFisrelativelysafeoutoftheboxhere,butthisprotectioniscompromisedassoonaswedeviatefromthesedefaults,forinstance,byusingstatelessviews.Seealsothesection“StatelessForms”inChapter4.

NexttothisimplicitCSRFprotection,JSFalsohasexplicitCSRFprotection.ThisexplicitCSRFprotectionaddsatokenforallcases,andadditionallyaddschecksforthe“referer”and“origin”HTTPheaders.NotethatHTTPheadersshouldnormallynotbetrustedforincomingrequests,astheycanbeveryeasilyspoofed.However,forthisparticularattackwe’renottryingtodefendagainstjustanyrandomHTTPrequestbutspecificallyagainstrequestssentfromatrustedbrowser.Theheaderchecksarealsoinadditiontothetokencheck,andthetokencheckmustalwayspassfirst.

Page 622: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

DefiningwhichviewsshouldbeprotectedbyaCSRFtokenandtheadditionalheadercheckshappensinawaythat’ssomewhatsimilartohowwedefinedrolesforviews—acollectionofURLpatternsinadeploymentdescriptor.Thistimethedeploymentdescriptorisfaces-config.xmlinsteadofweb.xmlthough.Thefollowinggivesanexamplefortheseentriesinfaces-config.xml:<protected-views>

<url-pattern>/bar.xhtml</url-pattern>

<url-pattern>/foo.xhtml</url-pattern>

<url-pattern>/baz.xhtml</url-pattern>

</protected-views>

Notethattheurl-patternhereistheexactsamepatternthatisusedinweb.xml.Onethingtobeawareofhereisthatdespitefaces-config.xmlbeingaJSF-specificdeploymentdescriptor,theurl-patternhereisagainforthefullURLrelativetotheapplicationrootwhichmeansithastoincludeallmappingsusedsuchasfacesand.xhtml.

Addingafaces-config.xmlwiththeaboveshownprotected-viewsfragmenttotheexampleapplicationcodewe’vebeenworkingoninthischapterwillrenderthelinkonindex.xhtmlto,forexample,/bar.xhtmlinthefollowingway:/bar.xhtml?javax.faces.Token=gdMoNbfOycv2v80gr

Ascanbeseen,thejavax.faces.TokenrequestparameterhasbeenaddedbyJSF.Thetokenistiedtotheuser’sHTTPsession,soiftheHTTPsessionexpires,thetokenalsoexpires.Thetokeniscryptographicallystrong,meaningthatitfulfillsallthreerequirementsforaCSRFprotection

Page 623: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

tokenasstatedabove.Ifthetokenistampered(e.g.,weuse

xxMoNbfOycv2v80grinsteadofgdMoNbfOycv2v80gr),orismissingaltogether(i.e.,werequest/bar.xhtml)JSFwillthrowajavax.faces.application.ProtectedViewExcep

tion.Incasethelinkoriginatedfromastatelesspage,JSFapplicationscanhandlethisexceptionbynotifyingtheuserandallowingtore-rendertheoriginalpageagain.Intheexampleabovethatwouldmeanre-renderingindex.xhtml.ThegenuineuserwouldgetanewCSRFprotectiontokenthen,whileanattackerwillnotgettheexpectedsideeffectfromtheoriginalrequest.

Asmentioned,there’sarefererandoriginheadercheckaswell.Forthischeck,JSFcheckswhethertherefererheader,ifavailable,issettoaURLthatoriginatesfromthesameapplication.Todemonstratethis,considerasecondJSFapplicationwithonlyanemptyfaces-config.xmlandthefollowingview:<!DOCTYPEhtml><htmllang="en"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

>

<h:head/>

<h:body>

<h:outputLink

value="http://localhost:8080projectbar.xhtml?

javax.faces.Token=gdMhNbfOycv2v80gr">

test

</h:outputLink>

Page 624: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</h:body>

</html>

AssumingourfirstapplicationwiththeCSRF-protectedbar.xhtmlisdeployedtohttp://localhost:8080/project,clickingtheoutputlinkfromthissecondapplicationwillcausethejavax.faces.application.ProtectedViewExcep

tiontobethrownagaininthefirstapplication.Notethattherefererisanoptionalcheckthat’sonlydonewhentherefererheaderisactuallypresent,asisthecasewiththelinkinthesecondapplication.

Ifweenterhttp://localhost:8080projectbar.xhtml?

javax.faces.Token=gdMhNbfOycv2v80grdirectlyintotheaddressbarofabrowser,orrequestitdirectlyviaacommand-lineutilitysuchaswgetorcurl,therewon’tbearefererandtherequestwillbeaccepted.

Inpractice,aGETrequesthaslessvalueofbeingprotectedagainstCSRFattackssincetheseactuallyshouldbeidempotent(shouldnothavesideeffectsandshouldonlydisplaydata).InsteadofshieldingGETrequestswithCSRFprotectiontokens,it’sprobablyafarbetterideatorefactoranapplicationtonothavenon-idempotentGETrequests.

ForpostbackstheCSRFprotectiontokenworksinmuchthesamewaythough.Todemonstrate,considerchangingthe/bar.xhtmlviewintothefollowing:<!DOCTYPEhtml><htmllang="en"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

>

Page 625: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<h:head/>

<h:body>

<h:form>

<h:commandButtonvalue="Test"action="#

{bar.submit}"/>

</h:form>

</h:body>

</html>

RenderingthispagewillresultintheformtargetURLhavingtheCSRFprotectiontokenappliedtoitinthesamewayaswesawfortheGETrequests—forexample,<formmethod="post"action="projectbar.xhtml?

javax.faces.Token=gdMhNbfOycv2v80gr"...>

Thisisthusanotherdifferencewiththeimplicitjavax.faces.ViewStatetoken,whichisalwaysaPOSTparameter.

WebParameterTamperingProtectionWebparametertamperingisanattackagainstanapplicationwhereanattackermodifiesparametervaluesthataresent(back)totheserverhostingtheapplication.Iftheapplicationdoesn’tvalidatethosevaluescorrectly,anattackercouldgainmorebenefitsthanentitledto,ormaygettheopportunitytocarryoutadditionalattacks.

Forexample,supposeawebapplicationrendersalistofrolesthatcanbeassignedtoanotheruser,say“user,”“manager,”and“sales.”Anattackercouldattempttomodifythedatapostedbackandchangetheselectionof“user”into“admin.”Iftheserverblindlyacceptstheinputand“admin”is

Page 626: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

anexistingvalue,thisallowstheattackertogiveanotheruserthe“admin”role,evenwhentheattackerisnotprivilegedtodothat.

JSFhasanimplicitprotectionagainstasubsetofthisattack;namely,againstvaluesbeingpostedbackfromaselection(specifically,fromUISelectOne-andUISelectMany-basedcomponents).ThisworksbyJSFeitherrestoringtheviewfromjavax.faces.ViewStateafterapostback(whenfullstatesavingisused)orre-creatingit(whenpartialstatesavingisused).Onlyvaluesthatwerealsorenderedareaccepted.Theusualcaveatappliesthough,andthat’sthatwithpartialstatesaving,thedataboundtothecomponentneedstobeidenticalbeforeandafterthepostback.This,however,caneasilybeaccomplishedbyusingtheviewscope.

Todemonstrate,considerthefollowingview:

<!DOCTYPEhtml>

<htmllang="en"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:f="http://xmlns.jcp.org/jsf/core"

>

<h:head/>

<h:body>

<h:form>

<h:selectOneMenuvalue="#{bean.selected}">

<f:selectItemsvalue="#{bean.available}"/>

</h:selectOneMenu>

<h:commandButtonvalue="Select"/>

</h:form>

<p>Chosenvalue:#{bean.selected}</p>

</h:body>

</html>

Page 627: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Andthefollowingbackingbean:

@Named@RequestScoped

publicclassBean{

privateList<String>available=Arrays.asList("foo",

"bar","kaz");

privateStringselected;

publicList<String>getAvailable(){

returnavailable;

}

publicStringgetSelected(){

returnselected;

}

publicvoidsetSelected(Stringselected){

this.selected=selected;

}

}

Choosing,forexample,“bar”andclicking“select”willrender“Chosenvalue:bar,”asexpected.Changingthevaluebeingsentcanbedoneinvariousways,forinstanceviaaninterceptingproxylikeBurpProxyorbyeditingthelivesourceviathedevelopertoolsfromabrowsersuchasChrome.

Page 628: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure13-1 TamperingtheselectedvalueinHTMLsourcecode

Selectingthe“foo”entryafterthechangeasshowninFigure13-1andclickingtheselectbuttonagainwillcausethe“foox”valuetobesenttotheapplication.ThisvaluewillberejectedbyJSFandasaresult“Chosenvalue:”willberendered,indicatingthatourtamperedvalueindeedhasnotbeenaccepted.

Cross-SiteScriptingProtectionCross-sitescriptingorXSSisanattackthathasacoupleofvariations,butpracticallyitboilsdowntoawebapplicationrenderingdatathatitgotfrom(other)usersdirectlyaspartofthemarkupsenttotheclient.Ifthisdataitselfcontainsscriptingcode(typicallyJavaScript),thebrowsermayexecuteitblindly,allowingtheattackertoread,forexample,cookiedataandtosendthatovertoaservercontrolledbytheattacker.

JSFprovidesprotectionagainstthistypeofattackby

Page 629: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

havingcontextualoutputescapingenabledformanycommoncontexts.ThemostcommoncontextiswritingoutHTML,whereallJSF’soutputwritersbydefaultXMLescapetheiroutput.

Todemonstrate,considerthefollowingbackingbean:

@Named@RequestScoped

publicclassBean{

privateStringvalue="<script>alert('hi')</script>";

publicStringgetValue(){

returnvalue;

}

}

Thevalueinstancevariablecontainsascriptthatwedon’twantthebrowsertoexecute.Inthisexampleit’shard-coded,butinpracticeitcouldcomefromstoreddatain,forexample,adatabase.

Nowwe’llrenderthisvalueusingtwosimpledefaultconstructsofJSF:adirectexpressionlanguageexpressiononaFaceletandthe<h:outputText>component.

<!DOCTYPEhtml>

<htmllang="en"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:f="http://xmlns.jcp.org/jsf/core"

>

<h:head/>

<h:body>

<p>#{bean.value}</p>

<p><h:outputTextvalue="#{bean.value}"><p>

</h:body>

Page 630: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</html>

WhenrequestingthisviewandlookingattheHTMLsource,we’llseethatbothtimesthevaluehasbeenrenderedas“<script>alert('hi')</script>”(i.e.,inanescapedformthatthebrowserwon’texecute).

Incaseweexplicitlydon’twantthisescapingtobedone,theescapeattributeof<h:outputText>canbesettofalse.Forexample,<h:outputTextvalue="#{bean.value}"escape="false"/>

RequestingtheviewagainwiththeabovecomponentonitwillcauseaJavaScriptalerttoappeartobesaying“hi.”Hadthisbeenmaliciouscodebeinginputbyanattacker,thesecurityoftheclient’ssystemwouldhavebeencompromised.Theescapeattributeshouldthereforeonlybeusedwiththeutmostcare.

OutputforusageinURLsisescapedaswell,butthereit’sescapeddifferently,sinceit’sadifferentcontext.Todemonstrate,consideraddingthefollowingcomponenttotheview:<h:linkoutcome="/foo">Gotofoo

<f:paramname="param"value="#{bean.value}"/>

</h:link>

Afterrequestingthisview,we’llseethelinkhasbeenrenderedas<ahref="projectfoo.xhtml?param=%3Cscript%3Ealert%28%27hi%27%29%3C%2Fscript%3E">

Gotofoo

</a>

Whathashappenedhereisthatouroriginalvaluehasbeen

Page 631: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

escapedforusageasaURLparameterusingURLencoding,whichascanbeseenisdifferentfromXMLescaping,hencetheterm"contextualoutputescaping."

RelatedtoXSSprotection,sensitivecookiesthattheapplicationusesshouldbesettoHttpOnly,meaningthey’llbesenttotheserverwitheachrequestbutcan’tbereadbyscriptsontheclient.ForthesessionIDcookiethiscanbedoneinweb.xmlasfollows:<session-config><cookie-config>

<http-only>true</http-only>

<secure>true</secure>

</cookie-config>

</session-config>

Notethatthecookieissetto"secure"aswellhere.ThisisnotrelatedtoXSS,butsetsthatthecookieistobesentonlywhenHTTPS/SSLisused.Thisprotectsthecookiefrombeingeavesdropped(e.g.,onasharedWiFinetwork).SincedevelopmentoftenhappensoverHTTPusinglocalhost,suchasettingmaybeproblematicfordevelopmentpurposes.Ifthisisthecase,thenalternativelythecookiecanbesettosecureornotusingServletContext#getSessionCookieConfig()inaServletContextListener

@WebListener

publicclassApplicationConfigimplements

ServletContextListener{

@Override

publicvoidcontextInitialized(ServletContextEventevent)

{

if(...){

Page 632: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

event.getServletContext()

.getSessionCookieConfig()

.setSecure(false);

}

}

}

where“...”isanapplication-specificchecktoseeifit’srunningin“devmode.”YoucouldevenuseJSF’sownApplication#getProjectStage()forthis.

SourceExposureProtectionSourceexposureinthecontextofserver-sidewebapplicationsreferstotheunwanteddisclosureofpartsofthewebapplication’ssource.Thisisasecurityrisk,notonlybecauseoftheexposureofthesourceitself(whichmaybeatradesecret)butalsobecauseitmaygiveanattackerinsightonwhichtobasefollow-upattacks(thesourcemaycontainreferencestoothersystems,beans,orevencommentswithpasswords,althoughthosekindsofcommentsshouldofcoursenotbetheretobeginwith).

Duetoanumberofsomewhatperhapsunfortunatedesignchoicesinthepast,JSFhassomespecificvulnerabilitieshere,whichmostlyconcernhowURLmappingisdonebutalsoconcernthelocationofresourcefiles.TounderstandthisvulnerabilitywefirstexplainhowJSFmappingandtheFacesServletwork.

ThemainentryintoeveryJSFapplicationistheFacesServlet.ThisisaServletprovidedbytheJSFframeworkthatactsasaso-calledfrontcontrollerthroughwhichallJSFrequestsarerouted.

Page 633: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ArequesttoaJSFviewsuchasfoo.jsfwillthusfirstneedtogothroughthisServlet,whichwilltheninsomewaylocatethedefinitionofthetreeofcomponentsthatrepresenttheviewfoo.jsf.We’llcallthisactualdefinitionthe“physicalresource.”Outofthebox,JSFsupportstwotypesofphysicalresources:FaceletsandJSPfileswiththeextension.xhtml,.view.xml,and.jsp.NotethatJSPfilesarelargelydeprecated.

InorderfortheFacesServlettobeabletohandlealltheserequestsithastobemappedtooneormoreURLpatternsthatcapturethem.Tobeabletodothis,JSFsupportsprefix,suffix,andexactmapping.

Ifnoexplicitmappinginweb.xmlisspecifiedandarecognizedJSFartifactisfoundintheapplication(suchasanemptyfaces-config,xmlorthe@FacesConfigannotation)thentheFacesServletisautomaticallymapped.SinceJSF2.1andServlet3.0thisautomaticmappingistothefollowingpatterns:

faces*(prefixmapping)

*.jsf(suffixmapping)

*.faces(suffixmapping)

SinceJSF2.3thisalsoincludes:*.xhtml(suffixmapping)

Especiallyolder(existing)JSFapplicationsstillexplicitlymaptheFacesServletinweb.xml,whichthenlooksforexampleasfollows:<servlet><servlet-name>facesServlet</servlet-name>

<servlet-

class>javax.faces.webapp.FacesServlet</servlet-class>

Page 634: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</servlet>

<servlet-mapping>

<servlet-name>facesServlet</servlet-name>

<url-pattern>faces*</url-pattern>

<url-pattern>*.jsf</url-pattern>

</servlet-mapping>

InsuffixmappingandwhenusingFacelets,theFacesServletwillfirsttrytolocatethephysicalresourcewiththesamepathandnameastherequestedresource,butwiththesuffixreplacedby.xhtml.Forexample,arequestforpathfoo.jsfwillresultinalookupforthefilepathfoo.xhtml.

Withprefixmapping,theFacesServletwilltrytolocatethephysicalresourcewiththesamenameandpathastherequestedresource,butminustheprefixpath.Forexample,arequestforfacespath/foo.xhtmlwillresultinalookupforthefilepathfoo.xhtml.

This,however,mayintroduceasecurityissuethatwillresultinexposureoftheFaceletsourcecode.Namely,thelookupforpathfoo.xhtmlisdoneinthewebrootoftheWARinwhichtheFacesServletresides.Unlessotherwise(implicitly)mapped,everyfileinthewebrootisdirectlyaccessiblefordownload.Inthiscase,ifpathfoo.xhtmlisdirectlyrequestedinsteadofpathfoo.jsforfacespath/foo.xhtmlthisrequestwillnotgothroughtheFacesServletandreturnsthebareFaceletssourcecodeofthatpageinsteadoftherenderedmarkup.

Therearetwowaystopreventthisexposureofsourcecode.

Page 635: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

1. MaptheFacesServletdirectlyto*.xhtml

2. Addasecurityconstrainttoweb.xml

MappingtheFacesServletdirectlyto*.xhtmlmaybethemostnaturalsolution.Withthismapping,therequestedresourceisidenticaltothephysicalresource.Ifpathfoo.xhtmlisrequested,thentheFacesServletwilltrytolocatepathfoo.xhtml.Viathismapping,thereisnosecondpathtoreachtheFaceletssourceandhencenoriskofexposingit.

Sidenote:beingabletouse*.xhtmlmappingwasanewfeatureinJSF2.0.DoingthisinJSF1.xresultedinaninfiniteloop.

TomaptheFacesServletto*.xhtml,theeasiestwayistorelyonthedefaultmappingofJSF2.3asexplainedabove.Ifthisisnotpossibleforsomereason,addthemappingtotheFacesServletmappinginweb.xmlasshowninthefollowingcode:<servlet-mapping><servlet-name>facesServlet</servlet-name>

<url-pattern>faces*</url-pattern

<url-pattern>*.jsf</url-pattern>

<url-pattern>*.xhtml</url-pattern>

</servlet-mapping>

Asanalternative,asecurityconstraintcanbedefinedthatpreventsaccessto*.xhtmlresources.Thereisrarelyagoodreasontopreferthisoverthesimpler*.xhtmlto*.xhtmlmapping,butforcompleteness,thiscanbedoneasfollows:<security-constraint>

<display-name>NoaccesstoFaceletssource</display-

name>

Page 636: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<web-resource-collection>

<web-resource-name>XHTML</web-resource-name>

<url-pattern>*.xhtml</url-pattern>

</web-resource-collection>

<auth-constraint/>

</security-constraint>

AspecialcaseofexposingFaceletssourcecodehappenswithcompositecomponents.CompositecomponentsarecomponentsthatareimplementedviaaFaceletinsteadofaJavaclass.Byconvention,theyhavetobeplacedinadirectoryinsideadirectorynamed/resourcesthatresidesinthewebroot—forinstance,/resources/bar/foo.xhtml.Thiswillmakeacomponent“foo”availableinthenamespace“http://xmlns.jcp.org/jsf/composite/bar”.

Componentsareofcoursenotviewsandtheusershouldnotbeabletorequestthosedirectly.Unfortunately,/resourcesisnotinanywayaspecialdirectorytoJavaEE.JSFassignsaspecialmeaningtoitbyconvention,buttotheServletcontainerit’sadirectorylikeanyother.Thisspecificallymeansthere’snoprotectionappliedtothisandanyusercandirectlyrequestresourcesfromit.Inotherwords,thisdirectoryis“worldreadable.”Evenwithan*.xhtmlmapping,thisnotonlyallowstheusertoguesswhichcomponentswehavebutletstheuserattempttoexecutethoseaswell.Clearlythisisnotwhatwewant.

Thereareagaintwosolutionsforthis:1. ConfigureanotherdirectorytobetheJSFresourcesdirectory

2. Addasecurityconstrainttoweb.xml

Page 637: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

InJSF2.2amethodwasintroducedtoaddressthissecurityvulnerability.Namely,viathejavax.faces.WEBAPP_RESOURCES_DIRECTORY

contextparameteranotherdirectorycanbeconfiguredtobetheJSFresourcesdirectoryinsteadof/resources.Forexample,<context-param><param-

name>javax.faces.WEBAPP_RESOURCES_DIRECTORY</param-name>

<param-value>WEB-INF/resources</param-value>

</context-param>

Notethatthepathisrelativetothewebrootandmaynotbeginwitha“/”.

Alternatively,orforJSF2.0/2.1,asecurityconstraintcanbeconfiguredinweb.xmlagainthatprohibitscalleraccessto/resources.Thiscanbedoneinasimilarwayasprotectingfor*.xhtmlaccess.

<security-constraint>

<web-resource-collection>

<web-resource-name>resources</web-resource-name>

<description>Theresourcesdirectory</description>

<url-pattern>/resources/*</url-pattern>

</web-resource-collection>

<auth-constraint/>

</security-constraint>

Page 638: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1) (2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_14

14.Localization

BaukeScholtz andArjanTijms

Willemstad,Curaçao Amsterdam,Noord-Holland,TheNetherlands

JSFhasalwayshaddecentinternationalizationsupport.SinceJSF1.0youcansupplyjava.util.ResourceBundle-basedbundlefilesindifferentlocales,whichinturngetdynamicallyincludedastextinthewebpageatthedeclaredplaces.Also,allJSFconvertersandvalidatorshavetheirownsetoflocalizeddefaultmessageswhichyoucaneasilycustomizeviaamessagebundleor,sinceJSF1.2,vianewrequiredMessage,converterMessage,andvalidatorMessageattributes.JSF2.0adds,viathenewjavax.faces.application.ResourceHandler,API(applicationprograminginterface)supportforlocalizableassetssuchasstylesheets,scripts,andimages.

Theactofinternationalization,“I18N,”isdistinctfromtheactoflocalization,“L10N.”TheinternationalizationpartisbasicallyalreadydonebyJSF(JavaServerFaces)itselfasbeingaMVC(model-view-controller)framework.Allyouneedtodoistotakecareofthelocalizationpart.Basically,youneedtospecifythe“activelocale”intheview,supplythe

1 2

Page 639: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

desiredresourcebundlefiles,ifnecessarytranslatedwithhelpofathird-partytranslationservice,anddeclarereferencestothebundlefileinyourJSFpage.

InthischapteryouwilllearnhowtoprepareaJSFwebapplicationfordifferentlanguagesandhowtodevelopitinordertomakelocalizationeasierforyourselfastomaintenance.

HelloWorld,Olámundo,

Tostartoff,createabunchofnewbundlefilesinmain/java/resourcesfolderoftheproject.Themain/java/resourcesfolderofaMavenWARprojectisintendedfornon-classfileswhicharesupposedtoendupintheWEB-INFclassesfolderofthefinalbuild.Thebundlefilescanbeinjava.util.Propertiesformat,withthe.propertiesextension.

Thefilenameofthosefilesmusthaveacommonprefix(e.g.,“text”),followedbyanunderscoreandthetwo-letterISO639-1-Alpha-2 languagecode(e.g.,“en”forEnglish,“pt”forPortuguese,and“hi”forHindi).Itcanoptionallybefollowedbyanotherunderscoreandthetwo-letterISO3166-1-Alpha-2 countrycode(e.g.,“GB”forGreatBritain,“US”forUnitedStates,“BR”forBrazil,“PT”forPortugal).

main/java/resources/com/example/project/i18n/text.properties

title=Localizationexample

heading=HelloWorld

paragraph=Welcometomywebsite!

main/java/resources/com/example/project/i18n/text_pt_BR.prope

1

2

Page 640: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

rties

title=Exemplodelocalização

heading=Olámundo

paragraph=Bem-vindoaomeusite!

main/java/resources/com/example/project/i18n/text_hi.properti

es

Donotethatallthosebundlefileshavecommonkeys“title”,“heading”,and“paragraph”,whichareusuallyinEnglish.It’sbasicallythelinguafrancaoftheInternetandwebdevelopers.It’sconsideredthebestpracticetokeepthesourcecodeentirelyinEnglish,particularlyifitisopensource.

AlsonotethattheEnglishbundlefiledoesn’thavethe“en”languagecodeinthefilenameasintext_en.propertiesbutisjusttext.properties.Basically,ithasbecomethefallbackbundlefilewhichissupposedtocontaineverysinglebundlekeyusedintheentirewebapplication.Thisway,whenabundlefilewithaspecificlanguagecodedoesn’tcontainthedesiredbundleentry,thenthevaluewillbelookedupfromthefallbackbundlefile.Thisisusefulforsituationswhereinyou’dliketograduallyupgradethebundlefiles,orwhenyouhavecertainsectionsinthewebapplicationwhichdon’tnecessarilyneedtobelocalized,suchasback-endadminpages.

ConfigurationInordertofamiliarizetheJSFapplicationwiththosebundle

Page 641: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

filesandthedesiredlocales,weneedtoedititsfaces-config.xmlfiletoaddthefollowingentriestothe<application>element:<application><locale-config>

<default-locale>en</default-locale>

<supported-locale>pt_BR</supported-locale>

<supported-locale>hi</supported-locale>

</locale-config>

<resource-bundle>

<base-name>com.example.project.i18n.text</base-

name>

<var>text</var>

</resource-bundle>

</application>

The<base-name>mustspecifythefullyqualifiedname(FQN)followingthesameconventionasforJavaclassesandthatitdoesn’tincludethefileextension.The<var>basicallydeclarestheEL(ExpressionLanguage)variablenameofthebundlefile.ThiswillmakethecurrentlyloadedresourcebundleavailableasaMap-likeobjectinELvia#{text}.Toavoidconflicts,youonlyneedtomakesurethatthisnameisn’talreadypossessedbyanymanagedbeanoranyimplicitELobjects.

ReferencingBundleinJSFPageIt’srelativelysimple,justtreat#{text}asaMapwiththebundlekeysasmapkeys.

<!DOCTYPEhtml>

Page 642: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<htmllang="#{view.locale.toLanguageTag()}"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html">

<h:head>

<title>#{text['title']}</title>

</h:head>

<h:body>

<h1>#{text['heading']}</h1>

<p>#{text['paragraph']}</p>

</h:body>

</html>

JSFwillalreadyautomaticallydeterminetheclosestmatchingactivelocalebasedontheHTTPAccept-Languageheader andsetitaslocalepropertyofUIViewRoot.TheAccept-Languageheaderisconfigurableinbrowser’ssettings.In,forexample,Chrome,youcanconfigureitviachrome://settings/languages.Ifyouplayaroundwithit,forexample,byswitchingbetweenEnglish,Portuguese,andHindiasthetop-rankedlanguagesettinginbrowserandrefreshtheJSFpage,thenyou’llnoticethatitchangesthetexttoconformthebrowser-specifiedlanguagesetting.Ifyoucheckthebrowser’sdevelopertools—usuallyaccessiblebypressingF12—andinspecttheHTTPrequestheadersinthenetworkmonitor,thenyou’llalsonoticethattheAccept-Languageheaderchangesaccordingly.

Youmighthavenoticedthatthelangattributeofthe<html>tagreferences#{view.locale.toLanguageTag()}.Basically,thiswillprinttheIETFBCP47languagetag ofthelocalepropertyofthecurrentUIViewRoot,whichisinturnavailableasanimplicitELobject#{view}.Thelocalepropertyisaninstanceofjava.util.Localewhichhas

3

4

Page 643: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

actuallynogettermethodforthelanguagetagsuchasgetLanguageTag(),butonlyatoLanguageTag()method,hencethedirectmethodreferenceinELinsteadoftheexpectedpropertyreference.

Thelangattributeofthe<html>tagisnotmandatoryforthefunctioningofJSFlocalizationfeature.Moreover,JSFtreatsitastemplatetextanddoesnothingspecialwithit.Youcansafelyleaveoutit.Itis,however,importantforsearchengines.ThiswayasearchenginelikeGooglewillbeinformedwhichlanguagethepage’scopyisin.Thisisnotonlyimportantinordertoendupcorrectlyinlocalizedsearchresults,butalsoimportantincaseyouservetheverysamepageindifferentlanguages.Thiswouldotherwisebytheaveragesearchenginealgorithmbepenalizedas“duplicatecontent,”whichisthusbadforSEO(searchengineoptimization)ranking.

You’llalsohavenoticedthatthebundlekeysarespecifiedintheso-calledbracenotation#{text['...']}.Thestringbetweensinglequotesbasicallyrepresentsthebundlekey.Inthisspecificcaseyoucouldalsohaveused#{text.title},#{text.heading},and#{text.paragraph}instead.Thisis,however,notthecommonpractice.UsingthebracenotationnotonlygivesagenerallyclearmeaningtowhattheELvariablerepresents(aresourcebundle),butitalsoallowsyoutousedotsinthebundlekeynamesuchas#{text['meta.description']}.TheELexpression#{text.meta.description}has,namely,anentirelydifferentmeaning:“getthedescriptionpropertyofthenestedmetapropertyofthetextobject,”whichisincorrect.

Page 644: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

ChangingtheActiveLocaleYoucanalsochangetheactivelocaleontheserverside.Thisisbesttobedoneinasingleplaceinasite-widemastertemplatewhichcontainsthe<f:view>tag.Theactivelocalecanbesetinthelocaleattributeofthe<f:view>whichcanaccepteitherastaticstringrepresentingthelanguagetagoraconcretejava.util.Localeinstance.ThelocaleattributeacceptsanELexpressionandcanbechangedprogrammaticallyviaamanagedbean.Thisoffersyoutheopportunitytolettheuserchangeitviathewebpagewithoutfiddlingaroundinthebrowser’slanguagesettings.YoucouldpresenttheavailablelanguageoptionstotheuserinaJSFpageandleteachselectionchangetheactivelocale.ThiscanbeachievedwiththefollowingJSFpage:<!DOCTYPEhtml><htmllang="#{activeLocale.languageTag}"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:f="http://xmlns.jcp.org/jsf/core"

xmlns:h="http://xmlns.jcp.org/jsf/html">

<f:viewlocale="#{activeLocale.current}">

<h:head>

<title>#{text['title']}</title>

</h:head>

<h:body>

<h1>#{text['heading']}</h1>

<p>#{text['paragraph']}</p>

<h:form>

<h:selectOneMenuvalue="#

{activeLocale.languageTag}">

<f:selectItems

value="#

Page 645: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

{activeLocale.available}"var="l"

itemValue="#{l.toLanguageTag()}"

itemLabel="#

{l.getDisplayLanguage(l)}">

</f:selectItems>

<f:ajaxlistener="#

{activeLocale.reload()}"/>

</h:selectOneMenu>

</h:form>

</h:body>

</f:view>

</html>

Itisslightlyadjustedfromthepreviousexample;thereisnow<f:view>around<h:head>and<h:body>.Thelocaleattributeof<f:view>referencesthecurrentlyactivelocaleviathe#{activeLocale}managedbean,whichisasfollows:@Named@SessionScopedpublicclassActiveLocaleimplementsSerializable{

privateLocalecurrent;

privateList<Locale>available;

@Inject

privateFacesContextcontext;

@PostConstruct

publicvoidinit(){

Applicationapp=context.getApplication();

current=

app.getViewHandler().calculateLocale(context);

Page 646: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

available=newArrayList<>();

available.add(app.getDefaultLocale());

app.getSupportedLocales().forEachRemaining(avail

able::add);

}

publicvoidreload(){

context.getPartialViewContext().getEvalScripts()

.add("location.replace(location)");

}

publicLocalegetCurrent(){

returncurrent;

}

publicStringgetLanguageTag(){

returncurrent.toLanguageTag();

}

publicvoidsetLanguageTag(StringlanguageTag){

current=Locale.forLanguageTag(languageTag);

}

publicList<Locale>getAvailable(){

returnavailable;

}

}

Toreiterate,@InjectFacesContextworksonlyifyouhaveplaced@FacesConfigonanarbitraryCDIbean

Page 647: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

somewhereinthewebapplication.OtherwiseyouhavetoreplaceitbyinlineFacesContext.getCurrentInstance()calls.There’sonlyonecaveatwiththoseinlinecalls:youneedtomakeabsolutelysurethatyoudon’tassignitasafieldin,forexample,@PostConstruct,becausetheactualinstanceissubjecttobeingchangedacrossmethodcallsontheverysamebeaninstance.InjectingasafieldviaCDItakestransparentlycareofthis,andisthereforesafe,butmanuallyassigningisnot.

In@PostConstruct,ViewHandler#calculateLocale()isusedtocalculatethecurrentlocalebasedonAccept-Languageheaderandthedefaultandsupportedlocalesasconfiguredinfaces-config.xml.ThisfollowsexactlythesameJSF-internalbehaviorasifwhenthere’sno<f:viewlocale>defined.Finally,theavailablelocalesarecollectedbasedontheconfigureddefaultandsupportedlocales.

Theavailablelocalesare,via<f:selectItems>of<h:selectOneMenu>,presentedtotheuserasdrop-downoptions(seeFigure14-1).Thenested<f:ajax>makessurethattheselectedoptionissetinthemanagedbeanassoonastheuserchangestheoption.

Page 648: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Figure14-1 Changingtheactivelocale

The#{activeLocale.languageTag}propertydelegatesinternallytothecurrentjava.util.Localeinstance.Thisisbasicallydoneforconveniencesothatwedon’tnecessarilyneedtoaddaconverterfor#{activeLocale.current}incasewewanttouseitin<h:selectOneMenu>.

<f:ajaxlistener>basicallyperformsafullpagereloadwiththehelpofapieceofJavaScriptwhichisexecutedoncompletionoftheAjaxrequest.ThisisdonebyaddingascripttothePartialViewContext#getEvalScripts()method,whichisnewsinceJSF2.3.Anyaddedscriptwillenduporderedinthe<eval>sectionoftheJSFAjaxresponse,whichinturngetsexecutedafterJSFAjaxenginehasupdatedtheHTMLDOM(DocumentObjectModel)tree.

Thescriptitself,location.replace(location),basicallyinstructsJavaScripttoreloadthecurrentdocumentwithoutkeepingthepreviousdocumentinhistory.Thismeans

Page 649: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

thatthebackbuttonwon’tredisplaythesamepage.Youcanalsouselocation.reload(true)instead,butthiswon’tworknicelyifasynchronous(non-Ajax)POSTrequesthasbeenfiredonthesamedocumentbeforehand.Itwouldbere-executedandcauseadoublesubmit.And,itunnecessarilyremembersthepreviouspageinthehistory.Thismayendupinconfusingbehavior,becausethebackbuttonwouldthenseemtohavenoeffectasitwouldredisplayexactlythesamepageastheactivelocaleisstoredinthesession,notintherequest.

Alternatively,insteadofinvoking<f:ajaxlistener>,youcaninthisspecificusecasealsousejust<f:ajaxrender="@all">withoutanylistener.Ithasatleastonedisadvantage:thedocument’stitlewon’tbeupdated.Inanycase,[email protected]’sonlyonelegitimatereal-worldusecaseforit:displayingafullerrorpageonanAjaxrequest.

Asacompletelydifferentalternative,youcouldmaketheactivelocalerequestscopedinsteadofsessionscopedbyincludingthelanguagetagintheURLasinhttp://example.com/en/page.xhtml,http://example.com/pt/page.xhtml,http://example.com/hi/page.xhtml.Thiswayyoucanchangetheactivelocalebysimplyfollowingalink.Thisonlyinvolvesaservletfilterwhichextractsthejava.util.LocaleinstancefromtheURLandforwardsittothedesiredJSFpage,andacustomviewhandlerwhichincludesthelanguagetaginthegeneratedURLofany<h:form>,<h:link>,and<h:button>component.YoucanfindakickoffexampleintheJavaEEKickoff

5

Page 650: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Application.

OrganizingBundleKeysWhenthewebapplicationgrows,youmaynoticethatbundlefilesstarttobecomeunmaintainable.Thekeyistoorganizethebundlekeysfollowingaverystrictconvention.Reusablesite-wideentries,usuallythoseusedasinputlabels,buttonlabels,linklabels,tableheaderlabels,etc.,shouldbekeyedusingageneralprefix(e.g.,“label.save=Save”).Page-specificentriesshouldbekeyedusingapage-specificprefix(e.g.,“foldername_pagename.title=SomePageTitle”).FollowingisanelaborateexampleofalocalizedFaceletstemplate,WEB-INFtemplates/page.xhtml:<!DOCTYPEhtml>

<htmllang="#{activeLocale.language}"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:f="http://xmlns.jcp.org/jsf/core"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"

xmlns:fn="http://xmlns.jcp.org/jsp/jstl/functions"

>

<c:setvar="page"value="page#{fn:replace(

fn:split(view.viewId,'.')[0],'','_')}"

scope="view">

<f:viewlocale="#{activeLocale.current}">

<h:head>

<title>#{text[page+='.title']}</title>

<metaname="description"

content="#{text[page+=

5

Page 651: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

'.meta.description']}"/>

</h:head>

<h:bodyid="#{page}">

<header>

<nav>

<h:linkoutcome="/home"

value="#{text['label.home']}"/>

<h:linkoutcome="/login"

value="#{text['label.login']}"

/>

<h:linkoutcome="/signup"

value="#{text['label.signup']}"

/>

</nav>

<h:form>

<h:selectOneMenu

value="#

{activeLocale.languageTag}">

<f:selectItems

value="#

{activeLocale.available}"var="l"

itemValue="#

{l.toLanguageTag()}"

itemLabel="#

{l.getDisplayLanguage(l)}">

</f:selectItems>

<f:ajaxlistener="#

{activeLocale.reload()}"/>

</h:selectOneMenu>

</h:form>

</header>

Page 652: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<main>

<h1>#{text[page+='.title']}</h1>

<ui:insertname="content"/>

</main>

<footer>

©#{text['page_home.title']}

</footer>

</h:body>

</f:view>

</html>

TheJSTL<c:set>basicallyconvertstheUIViewRoot#getViewId()toastringwhichissuitableasapage-specificprefix.TheJSFviewIDbasicallyrepresentstheabsoluteserver-sidepathtothephysicalfilerepresentingtheJSFpage(e.g.,“useraccount.xhtml”).Thisneedstobemanipulatedtoaformatsuitableasaresourcebundlekey.Thefn:split()callextractsthepart“useraccount”fromitandthefn:replace()callconvertstheforwardslashtounderscoresothatitbecomes“useraccount”.Finally,<c:set>storesitas“pageuseraccount”intheviewscopeunderthename“page”sothatit’savailableas#{page}elsewhereinthesameview.

You’llnoticethat#{page}isinturnbeingusedas,amongothers,theIDof<h:body>.ThismakesiteasiertoselectaspecificpagefromageneralCSS(CascadingStyleSheets)filejustincasethat’sneeded.#{page}isalsobeingusedinseveralresourcebundlereferences,suchas#{text[page+='.title']}whichultimatelyreferencesincaseof“/home.xhtml”thekey

Page 653: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

“page_home.title”.Withsuchatemplateyoucanhavethefollowingpage-specificresourcebundleentries:

page_home.title=MyWebsite

page_home.meta.description=AHelloWorldJSFapplication.

page_login.title=LogIn

page_login.meta.description=LogintoMyWebsite.

page_signup.title=SignUp

page_signup.meta.description=SignuptoMyWebsite.

FollowingisanexampleofatemplateclientwhichutilizesthepreviouslyshowntemplateWEB-INFtemplates/page.xhtml,the/login.xhtml:<ui:compositiontemplate="WEB-INFtemplates/page.xhtml"

xmlns:="http://www.w3.org/1999/xhtml"

xmlns:f="http://xmlns.jcp.org/jsf/core"

xmlns:h="http://xmlns.jcp.org/jsf/html"

xmlns:ui="http://xmlns.jcp.org/jsf/facelets"

>

<ui:definename="content">

<h:formid="login">

<fieldset>

<h:outputLabelfor="email"

value="#{text['label.email']}"/>

<h:inputTextid="email"required="true"

value="#{login.email}"/>

<h:messagefor="email"

styleClass="message"/>

<h:outputLabelfor="password"

Page 654: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

value="#{text['label.password']}"/>

<h:inputSecretid="password"

required="true"

value="#{login.password}"/>

<h:messagefor="password"

styleClass="message"/>

<h:commandButtonid="submit"action="#

{login.submit}"

value="#{text['label.login']}"/>

<h:messagefor="login"

styleClass="message"/>

</fieldset>

</h:form>

</ui:define>

</ui:composition>

Followingiswhattheassociatedresourcebundleentrieslooklike:

label.email=Email

label.password=Password

label.login=LogIn

Note:incaseyoufindthatthepagelookscrippled,simplyaddaCSSfilewiththefollowingruletostartwith:nava,fieldsetlabel,fieldsetinput{

display:block;

}

LocalizingConversion/ValidationMessages

Page 655: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

IncaseyouhavepreparedasimplebackingbeanclassLoginwithtwostringpropertiesemailandpasswordandamethodsubmit(),andsubmittheaboveshownloginpagewithoutfillingoutthee-mailinputfield,thenyou’llfaceavalidationerrorinthefollowingformat:login:email:ValidationError:Valueisrequired.

WhenyouswitchthelanguagetoPortugueseandresubmittheemptyform,thenyou’llseethatit’salsolocalized.However,whenyouswitchthelanguagefurthertoHindi,thenyou’llnoticethatthere’snostandardHindimessagebundleinthestandardJSFimplementation.You’dneedtoprovideyourown.Thereareseveralwaystoachievethis.

First,JSFinputandselectcomponentssupportthreeattributestooverridethedefaultmessage:requiredMessage,validatorMessage,andconverterMessage.Thefollowingexampleshowshowtooverridethedefaultrequiredmessage:<h:inputText...requiredMessage="#{text['message.required']}"/>

Thisisarguablytheeasiestapproach.Themajorcaveatisthatyouhavetocopy/pasteiteverywhereincaseyouhaven’twrappeditinareusabletagfilelike<my:inputText>.ThisisnotDRY.

AnotherwayistosupplyacustommessagebundlewhichoverridesallpredefinedbundlekeysspecificforJSFconversion/validationmessagesandregisteritas<message-bundle>infaces-config.xml.Youcanfindthepredefinedbundlekeysinchapter2.5.2.4“LocalizedApplicationMessages”oftheJSFspecification. Thebundlekeyofthedefaultrequiredmessage“ValidationError:Value

6

7

Page 656: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

isRequired”isthusjavax.faces.component.UIInput.REQUIRED.Wecanadjustitinnewmessagebundlefilesasfollows:

main/java/resources/com/example/project/i18n/messages.propert

ies

javax.faces.component.UIInput.REQUIRED={0}isrequired.

main/java/resources/com/example/project/i18n/messages_pt_BR.p

roperties

javax.faces.component.UIInput.REQUIRED={0}éobrigatório.

main/java/resources/com/example/project/i18n/messages_hi.prop

erties

Finally,configureitinthe<application>elementofthefaces-config.xmlfile:<application>...

<message-

bundle>com.example.project.i18n.messages</message-

bundle>

</application>

You’llperhapshavenoticedthe{0}placeholdersinthemessages.Theyrepresentthelabelsoftheassociatedinputandselectcomponents.Thelabelsdefaulttothecomponent’sclientID,whichisbasicallytheIDoftheJSF-generatedHTMLelementasyoucanfindinthebrowser’spagesource.Youcanoverrideitbyexplicitlysettingthelabelattributeofthecomponent.

<h:inputTextid="email"...label="#{text['label.email']}"/>

<h:inputSecretid="password"...label="#

{text['label.password']}"/>

Page 657: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Notethatputtingthemessagebundleinadifferentfilethantheresourcebundleisnotstrictlynecessary.Youcanalsojustputthemessagebundleentriesintext.propertiesfilesandadjustthe<message-bundle>entrytopointtothesameFQNas<resource-bundle>.

ObtainingLocalizedMessageinaCustomConverter/ValidatorThevalueofthe<message-bundle>entrycanbeobtainedprogrammaticallyviaApplication#getMessageBundle().Youcaninturnuseittoobtaintheactualbundleviathejava.util.ResourceBundleAPI,alongwithUIViewRoot#getLocale().Thisallowsyoutoobtainalocalizedmessageinacustomconverterandvalidator.Followingisanexampleofsuchavalidator,whichchecksifthespecifiede-mailaddressisalreadyinuse:@FacesValidator(value="duplicateEmailValidator",

managed=true)

publicclassDuplicateEmailValidatorimplements

Validator<String>{

@Inject

privateUserServiceuserService;

@Override

publicvoidvalidate

(FacesContextcontext,UIComponentcomponent,

Stringvalue)

Page 658: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

throwsValidatorException

{

if(value==null){

return;

}

Optional<User>user=

userService.findByEmail(value);

if(user.isPresent()){

thrownewValidatorException(new

FacesMessage(getMessage(

context,

"message.duplicateEmailValidator")));

}

}

publicstaticStringgetMessage(FacesContext

context,Stringkey){

returnResourceBundle.getBundle(

context.getApplication().getMessageBundle(),

context.getViewRoot().getLocale()).getString

(key);

}

}

Youmighthavenoticedthenewmanagedattributeofthe@FacesValidatorannotation.ThiswillbasicallyturnonCDIsupportonthevalidatorinstanceandhenceallowyoutoinjectabusinessserviceintoavalidator.Thesameattributeisalsoavailablefor@FacesConverter.

Page 659: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Theshownvalidatorexampleassumesthatthefollowingentryispresentintheresourcebundlefilesasidentifiedby<message-bundle>:message.duplicateEmailValidator=Emailisalreadyinuse.

LocalizingEnumsThecleanestapproachtolocalizeenumsistosimplyusetheirownidentityasabundlekey.ThiskeepstheenumclassfreeofpotentialUI-specificcluttersuchashard-codedbundlekeys.Generally,thecombinationoftheenum’ssimplenameandtheenumvalueshouldsufficetorepresentasite-wideuniqueidentifier(UI).Giventhefollowingcom.example.project.model.Groupenumrepresentingausergroup:publicenumGroup{USER,

MANAGER,

ADMINISTRATOR,

DEVELOPER;

}

andthefollowingresourcebundleentriesintext.properties:Group.USER=UserGroup.MANAGER=Manager

Group.ADMINISTRATOR=Administrator

Group.DEVELOPER=Developer

youcaneasilylocalizethemasfollows:

<f:metadata>

<f:importConstantstype="com.example.project.model.Group"

/>

Page 660: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

</f:metadata>

...

<h:selectManyCheckboxvalue="#{editUserBacking.user.groups}">

<f:selectItemsvalue="#{Group.values()}"var="group"

itemLabel="#{text['Group.'+=group]}"/>

</h:selectManyCheckbox>

Notethatthe<f:importConstants>isnewsinceJSF2.3.Itisrequiredtobeplacedinside<f:metadata>.Itstypeattributemustrepresentthefullyqualifiednameoftheenumoranyclassorinterfacewhichcontainspublicconstantsinflavorofpublicstaticfinalfields.<f:importConstants>willautomaticallyimportthemintotheELscopeasaMap<String,Object>whereinthemapkeyrepresentsthenameoftheconstantasstringandthemapvaluerepresentstheactualvalueoftheconstant.

With#{Group.values()}youcanthusobtainacollectionofallconstantvaluesandeachvalueisthenlocalizedinitemLabel.AlsonotethatitemValueisomittedasitdefaultstothevalueofthevarattributewhichisalreadysufficient.

ParameterizedResourceBundleValuesYoucanalsoparameterizeyourresourcebundleentriesusingthe{0}placeholdersofthejava.text.MessageFormatAPI. TheycanontheJSFsideonlybesubstitutedwith<h:outputFormat>wherebytheparametersareprovidedas<f:param>children,inthesameorderastheplaceholders.Giventhefollowingentry:page_products.table.header=There{0,choice,0#areno

products

8

Page 661: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

|1#isone

product

|1<are{0}

products}.

itcanbesubstitutedusing<h:outputFormat>asfollows:<h:outputFormatvalue="#

{text['page_products.table.header']}">

<f:paramvalue="#{bean.products.size()}"/>

</h:outputFormat>

Database-BasedResourceBundleJSFalsosupportsspecifyingacustomResourceBundleimplementationas<base-name>.Thisallowsyoutoprogrammaticallyfillandsupplythedesiredbundles,forexample,frommultiplebundlefiles,orevenfromadatabase.Inthisexamplewe’llreplacethedefaultpropertiesfile-basedresourcebundlebyonewhichloadstheentriesfromadatabase.Thistakesusastepfurtherastoorganizingtheresourcebundlekeys.Thisway,youcaneveneditthemviaaweb-basedinterface.FollowingiswhattheJPAentitylookslike:

@Entity

@Table(uniqueConstraints={

@UniqueConstraint(columnNames={"locale","key"})

})

publicclassTranslation{

@Id@GeneratedValue(strategy=GenerationType.IDENTITY)

privateLongid;

@Column(length=5,nullable=false)

Page 662: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

private@NotNullLocalelocale;

@Column(length=255,nullable=false)

private@NotNullStringkey;

@Lob@Column(nullable=false)

private@NotNullStringvalue;

//Add/generategettersandsettershere.

}

NotethattheJPA(JavaPersistenceAPI)annotationsprovidesufficienthintsastowhattheDDL(DataDefinitionLanguage)ofthetableshouldlooklike.Whenhavingthepropertyjavax.persistence.schema-generation.database.actionsettocreateordrop-and-createinpersistence.xml,thenitwillautomaticallygeneratetheproperDDL.Forsakeofcompleteness,hereitisinHSQL/pgSQLflavor.

CREATETABLETranslation(

idBIGINTGENERATEDBYDEFAULTASIDENTITYPRIMARYKEY,

localeVARCHAR(5)NOTNULL,

keyVARCHAR(255)NOTNULL,

valueCLOBNOTNULL

);

ALTERTABLETranslation

ADDCONSTRAINTUK_Translation_locale_key

UNIQUE(locale,key);

Andhere’swhattheEJB(EnterpriseJavaBeans)servicelookslike.

@Stateless

Page 663: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicclassTranslationService{

@PersistenceContext

privateEntityManagerentityManager;

@TransactionAttribute(value=REQUIRES_NEW)

@SuppressWarnings("unchecked")

publicObject[][]getContent

(Localelocale,Localefallback)

{

List<Object[]>resultList=

entityManager.createQuery(

"SELECTt1.key,COALESCE(t2.value,t1.value)"

+"FROMTranslationt1"

+"LEFTOUTERJOINTranslationt2"

+"ONt2.key=t1.key"

+"ANDt2.locale=:locale"

+"WHEREt1.locale=:fallback")

.setParameter("locale",locale)

.setParameter("fallback",fallback)

.getResultList();

returnresultList.toArray(new

Object[resultList.size()][]);

}

}

ForJPAweonlyneedanadditionalconverterwhichconvertsbetweenjava.util.LocaleinthemodelandVARCHARinthedatabase,whichisrepresentedbyjava.lang.String.YoucanusetheJPA2.0AttributeConverterforthis.It’smuchlikeaJSFconverterbutforJPAentities.It’srelativelysimple;there’snoadditionalconfigurationnecessary.Seethefollowing:publicclassLocaleConverter

implementsAttributeConverter<Locale,String>

{

Page 664: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Override

publicStringconvertToDatabaseColumn(Localelocale)

{

returnlocale.toLanguageTag();

}

@Override

publicLocaleconvertToEntityAttribute(String

languageTag){

returnLocale.forLanguageTag(languageTag);

}

}

NowwehavethecustomResourceBundle;it’scalledDatabaseResourceBundle.Putitinthepackagecom.example.project.i18n.

publicclassDatabaseResourceBundleextendsResourceBundle{

privatestaticfinalControlCONTROL=new

DatabaseControl();

@Override

publicObjecthandleGetObject(Stringkey){

returngetCurrentInstance().getObject(key);

}

@Override

publicEnumeration<String>getKeys(){

returngetCurrentInstance().getKeys();

}

privateResourceBundlegetCurrentInstance(){

FacesContextcontext=

FacesContext.getCurrentInstance();

Page 665: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Stringkey=CONTROL.getClass().getName();

return(ResourceBundle)context.getAttributes()

.computeIfAbsent(key,k->

ResourceBundle.getBundle(key,

context.getViewRoot().getLocale(),

Thread.currentThread().getContextClassLoader(

),

CONTROL));

}

privatestaticclassDatabaseControlextendsControl{

@Override

publicResourceBundlenewBundle

(StringbaseName,Localelocale,Stringformat,

ClassLoaderloader,booleanreload)

throwsIllegalAccessException,

InstantiationException,

IOException

{

FacesContextcontext=

FacesContext.getCurrentInstance();

finalObject[][]contents=CDI.current()

.select(TranslationService.class).get()

.getContent(

locale,

context.getApplication().getDefaultLocale

());

returnnewListResourceBundle(){

@Override

protectedObject[][]getContents(){

returncontents;

}

};

}

}

}

Finally,adjustthe<resource-bundle><base-name>entryinfaces-config.xmltospecifythefullyqualified

Page 666: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

nameofthecustomResourceBundleasfollows:<base-name>com.example.project.i18n.DatabaseResourceBundle</ba

se-name>

TheactualimplementationofthisResourceBundleisfranklysomewhathacky,onlyandonlybecauseofthefollowinglimitations:

1. JSFdoesn’tallowdefiningacustomResourceBundle.Controlviafaces-config.xml.

2. ProvidingacustomResourceBundle.ControlviaSPI(SerialPeripheralInterface)asjava.util.spi.ResourceBundleControlProviderdoesn’tworkfromWARon.

3. CreatemultipleseparateDatabaseResourceBundlesubclassesforeachsinglelocaleregisteredinfaces-config.xml,suchasDatabaseResourceBundle_en,DatabaseResourceBundle_pt_BR,andDataBaseResourceBundle_hi,inordertosatisfythedefaultResourceBundle.Controlbehaviorisnotmaintenancefriendlyinlongterm.

AnadditionaladvantageofthisapproachisthatitallowsyoutoprogrammaticallyclearoutanydatabasebundlesinthecachebysimplycallingResourceBundle#clearCache().Namely,theJSFimplementationmayinturncacheitinitsApplicationimplementation,causingtheResourceBundle#clearCache()toseemtohavenoeffectatall.Mojarraisknowntodothat.

HTMLinResourceBundleThisisabadpractice.Itaddsamaintenanceburden.Forlarge

9

Page 667: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

sectionsofcontentyou’dbetterpickamorelightweightmarkuplanguagethanHTML,suchasMarkdown. ThisisnotonlysaferastoXSS(cross-sitescripting)risksbutalsoeasierfortheusertoeditviaatextareainsomeContentManagementSystem(CMS)screens.Thisisbesttoimplementincombinationwithadatabase-basedresourcebundle.YoucouldaddanextrabooleanflagtotheTranslationmodelindicatingwhetherthevalueshouldbeparsedasMarkdown.

@Column(nullable=false)

privatebooleanmarkdown;

Then,insideTranslationService#getContents(),selectitasthethirdcolumn.

"SELECTt1.key,COALESCE(t2.value,t1.value),t1.markdown"

And,finally,intheDatabaseControl#newBundle()method,afterretrievingthecontents,youcouldpostprocessthembasedontheboolean.YoucoulduseanyJava-basedMarkdownlibraryforthis,suchasCommonMark.

staticfinalParserPARSER=Parser.builder().build();

staticfinalHtmlRendererRENDERER=

HtmlRenderer.builder().build();

...

for(Object[]translation:contents){

if((boolean)translation[2]){

translation[1]=

RENDERER.render(PARSER.parse(translation[1]));

}

}

10

11

Page 668: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Footnotes1https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes.

2https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2.

3https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4.

4https://en.wikipedia.org/wiki/IETF_language_tag.

5https://github.com/javaeekickoff/java-ee-kickoff-app.

6https://en.wikipedia.org/wiki/Don%27t_repeat_yourself.

7http://download.oracle.com/otn-pub/jcp/jsf-2_3-final-

eval-spec/JSF_2.3.pdf.

8

https://docs.oracle.com/javase/8/docs/api/java/text/Mess

ageFormat.html.

9https://stackoverflow.com/q/4325164/157882.

10https://en.wikipedia.org/wiki/Markdown.

11https://github.com/atlassian/commonmark-java.

Page 669: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(1) (2)

©BaukeScholtz,ArjanTijms2018BaukeScholtzandArjanTijms,TheDefinitiveGuidetoJSFinJavaEE8,https://doi.org/10.1007/978-1-4842-3387-0_15

15.Extensions

BaukeScholtz andArjanTijms

Willemstad,Curaçao Amsterdam,Noord-Holland,TheNetherlands

IfthereisonesingleelementorvirtueofJSF(JavaServerFaces)towhichwecanattributeitslastingforsolong,it’sprobablyitsabilitytobeextendedinalargevarietyofways.FromtheonsetJSFmadeitpossiblytohavemostofitscoreelementsreplaced,decorated,oraugmented.

Thisgaverisetoalargenumberofextensionlibrariesandprojects.IntheveryearlydaysthesewereA4J(Ajax4JSF),Tomahawk,RichFaces,thestand-aloneFaceletsproject,PrettyFaces,andmany,manymore.A4JwasmergedintoRichFaces,andRichFacesitselfwaseventuallysunsetin2016.FaceletswasincorporatedintoJSFitself,whilePrettyFacesbecamepartoftheRewriteframework.Thesedayswell-knownandactiveextensionlibrariesarePrimeFaces,OmniFaces,andBootsFaces,amongothers.Whileindividuallibrarieshavecomeandgone,themainconstantistheextensibilityofJSFfromitsfirstdaysuntilthepresent.

It’ssometimessaidthatallthoselibrariesaddressdefects

1 2

Page 670: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

oromissionsinJSF,butthisisnotentirelyaccurate.Infact,JSFwasexplicitlydesignedtomakesuchextensionspossibleandthereforetoallow,evenstimulate,suchextensionlibrariestoappear.Forinstance,acontemporarypeertechnologyofJSF,EJB(EnterpriseJavaBeans),hadfewtonoextensionpointsand,thus,despiteitsmanyshortcomings,weneversawmuchofanecosystemflourisharoundit.

ExtensionTypesThereareacoupleofdifferentwaysbywhichtousethevariousextensionpointsinJSF.Amajordistinctionisbetweenthe“classical”approachandthe“CDI-centricapproach.”

Inthelatterapproachthere’sverylittletonothingthatJSFhastoexplicitlysupportextensibillity,asCDIhasanumberofmechanismsbuiltintosupportextendingorreplacingCDIartifacts.ThisistheplannedfutureforJSF(makingmostifnoteverythingaCDIartifact),butforthemomentJSF2.3isinanearlytransitionalphaseandonlyafewartifactsarevendedviaCDI.Table8-1inChapter8showedtheseartifacts.

ExtendingCDIArtifactsOneofthewaysCDIaugmentsorreplacesatypefullyistoprovideanalternativeproducer.WealreadysawthistechniquebeingusedinChapter13,albeitforaslightlydifferentusecase.

Theeasiestwayisifyouneedtofullyreplacethetype.Ifaugmentingisneeded,somecodeisnecessarytoobtaintheprevioustype,whichwiththecurrentversionofCDI(2.0)is

Page 671: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

slightlyverbose.Thefollowingshowsanexamplewherewereplacethe

requestparametermapwithanewmapthathasallthevaluesoftheoriginalmap,plusanadditionalvaluethatweaddourselves:

@Dependent@Alternative@Priority(APPLICATION)

publicclassRequestParameterMapProducer{

@Produces@RequestScoped@RequestParameterMap

publicMap<String,String>producer(BeanManager

beanManager){

Map<String,String>previousMap=

getPreviousMap(beanManager);

Map<String,String>newMap=newHashMap<>

(previousMap);

newMap.put("test","myTestValue");

returnnewMap;

}

}

ThegetPreviousMap()methodis,asmentioned,somewhatverbose.It’sdefinedasfollows:

privateMap<String,String>getPreviousMap(BeanManager

beanManager){

classRequestParameterMapAnnotationLiteral

extendsAnnotationLiteral<RequestParameterMap>

implementsRequestParameterMap

{

privatestaticfinallongserialVersionUID=1L;

}

TypeMAP_TYPE=newParameterizedType(){

@Override

publicTypegetRawType(){

returnMap.class;

Page 672: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

@Override

publicType[]getActualTypeArguments(){

returnnewType[]{String.class,String.class};

}

@Override

publicTypegetOwnerType(){

returnnull;

}

};

return(Map<String,String>)beanManager

.getReference(beanManager

.resolve(beanManager

.getBeans(MAP_TYPE,new

RequestParameterMapAnnotationLiteral())

.stream()

.filter(bean->bean

.getBeanClass()!=

RequestParameterMapProducer.class)

.collect(Collectors.toSet())),

MAP_TYPE,

beanManager.createCreationalContext(null));

}

}

It’sexpectedthatthetaskofobtainingthis“previous”or“original”typewillbemadeeasierinafuturerevisionofanyofthespecsinvolved.Forinstance,afutureversionofJSFwilllikelyintroduceready-to-useannotationliteralsforits(CDI)annotations,suchastheRequestParameterMapAnnotationLiteralshownhere.

Inordertotestthatthisalternativeproducerworks,considerthefollowingbackingbean:

@Named@RequestScoped

Page 673: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicclassTestBean{

@Inject@RequestParameterMap

privateMap<String,String>requestParameterMap;

publicStringgetTest(){

returnrequestParameterMap.get("test");

}

publicStringgetFoo(){

returnrequestParameterMap.get("foo");

}

}

AndthefollowingFacelet:

<!DOCTYPEhtml>

<htmllang="en"

xmlns="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

>

<h:head/>

<h:body>

<p>Test:#{testBean.test}</p>

<p>Foo:#{testBean.foo}</p>

</h:body>

</html>

Deployinganapplicationcontainingtheseartifactswitharequestparameterof,say,“foo=bar”,willrevealthatthenewmapindeedcontainstheoriginalrequestparametersaswellasthevaluethatweaddedourselves.

ExtendingClassicalArtifactsTheclassicalapproachtoaugmentorfullyreplaceatypein

Page 674: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

JSFisbyinstallingafactoryforthattype.ThebasicwaysuchafactoryworksisinbroadlinesidenticaltotheCDIapproachdemonstratedabove;thefactoryreturnsanimplementationoftherequestedtypeandobtainsareferencetothe“previous”or“original”type.

BeingclassicalinJavaEEtypicallymeansXML,andindeedtheclassicalfactoryinvolvesXML.Specifically,registeringafactoryentailsusingthe<factory>elementinfaces-config.xmlandaspecificelementpertypeforwhichafactoryistobeprovided.AsofJSF2.3thefollowingfactoriesaresupported:

<application-factory>

<exception-handler-factory>

<external-context-factory>

<faces-context-factory>

<facelet-cache-factory>

<partial-view-context-factory>

<lifecycle-factory>

<view-declaration-language-factory>

<tag-handler-delegate-factory>

<render-kit-factory>

<visit-context-factory>

<flash-factory>

<flow-handler-factory>

<client-window-factory>

<search-expression-context-factory>

Nexttothese,thereareanothernumberofartifactsthatcanbereplaced/augmentedinasomewhatsimilarbutstilldifferentway;herethere’snofactoryreturningthetype,butan

Page 675: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

implementationofthetypeisspecifieddirectly.Thisvariantisspecifiedusingthe<application>elementinfaces-config.xml.AsofJSF2.3thefollowingtypescanbereplaced/augmenteddirectly:

<navigation-handler>

<view-handler>

<resource-handler>

<search-expression-handler>

<flow-handler>

<state-manager>

<action-listener>

Notethatallofthesearesingletons.FromtheJSFruntimepointofviewthere’sonlyoneofeach,butmultipleimplementationseachaddingsomethingaresupportedbymeansofwrapping,whichthereforeformsachain(implementationAwrappingimplementationB,wrappingimplementationC,etc.).

Theabovespecificelementsusedforeachtypeimmediatelyhighlightanissuewiththeclassicalapproach;JSFhastoprovideexplicitsupportforeachspecifictypetobereplaced/augmentedinthisway.Bycontrast,theCDIapproachallowsustoprettymuchreplace/augmentanytypewithoutrequiringanyspecialsupportfromJSFotherthanthatJSFusesCDIforthatartifact.

Onthebrightside,thefactoryimplementationiscurrentlysomewhatsimplercomparedtotheCDIversion,asthe“previous”or“original”typeissimplybeingpassedtoitinitsconstructorinsteadofhavingtobelookedupusingverbosecode.

Asanexample,we’llshowhowtoaugmenttheexternal

Page 676: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

contextfactory.Forthiswestartwiththementionedregistrationinfaces-config.xml.

<factory>

<external-context-factory>

com.example.project.ExternalContextProducer

</external-context-factory>

</factory>

Theimplementationthenlooksasfollows:publicclassExternalContextProducerextendsExternalContextFactory{

public

ExternalContextProducer(ExternalContextFactorywrapped)

{

super(wrapped);

}

@Override

publicExternalContextgetExternalContext

(Objectcontext,Objectrequest,Object

response)

{

ExternalContextpreviousExternalContext=

getWrapped().getExternalContext(context,

request,response);

ExternalContextnewExternalContext=

new

ExternalContextWrapper(previousExternalContext){

@Override

publicStringgetAuthType(){

return"OurOwnAuthType";

Page 677: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

};

returnnewExternalContext;

}

}

Thereareafewthingstoobservehere.Firstofall,everyfactoryofthiskindhastoinheritfromapre-describedparentfactory,whichinthiscaseisExternalContextFactory.Second,there’sanimplicitcontractthatmustbefollowed,andthat’simplementingtheconstructorexactlyasshownintheexample.Thatis,addapublicconstructorwithasingleparametertheexactsametypeasthesuperclass,andpassthisparameterontothesuperconstructor.ThisinstanceisthenavailableinothermethodsusingthegetWrapped()method.

Testingthatthisindeedworksisrelativelyeasy.WeuseasimilarbackingbeanaswiththeCDIversion:

@Named@RequestScoped

publicclassTestBean{

@Inject

privateExternalContextexternalContext;

publicStringgetAuth(){

returnexternalContext.getAuthType();

}

}

AndthefollowingFacelet:

<!DOCTYPEhtml>

<htmllang="en"

Page 678: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

xmlns="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

>

<h:head/>

<h:body>

<p>Test:#{testBean.auth}</p>

</h:body>

</html>

Astheexternalcontextisalsoatypethat’sinjectableviaCDI,theobservantreadermaywonderwhathappenswhenbothaCDIalternativeproducerandaclassicfactoryareprovidedforthattype.Theansweristhatthisisstrictlyspeakingnotspecified(thusundefinedbehavior),yetinpracticeit’sstronglyimpliedthattheclassicfactoryisusedasthesourcetoultimatelygettheexternalcontextfrom.ThismeansthatanalternativeproducerforExternalContextwillonlyaffectthedirectinjectionofExternalContext,andnotanysituationwhenthistypeisobtainedinanyotherway,forinstance,bycallingFacesContext#getExternalContext().Thisissomethingusersshouldclearlybeawareof.Theexpectationis,though,thatafuturerevisionofthespecwillmakeaCDIproducertheinitialsource.

Plug-insAdifferenttypeofextendingthatJSFoffersnexttothealternativeproducersandfactoriesiswhat’sessentiallyaplug-in.Here,nocoreJSFtypeisreplacedoraugmented,butanadditionalfunctionalityisaddedtosomepartoftheruntime.Mostoftheseadditions,therefore,havetodeclareinsomewaywhatitistheyareexactlyadding,whichisdifferentfrom

Page 679: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

thefactorieswhichjustprovidedanimplementationoftypeXorY.

Plug-insareaddedaselementsofthe<application>elementinfaces-config.xml,justassomeofthefactory-liketypesmentionedabove.Thefollowingaresupported:

<el-resolver>

<property-resolver>(deprecated)

<variable-resolver>(deprecated)

<search-keyword-resolver>

WealreadysawanexampleoftheSearchKeywordResolverinthesection“CustomSearchKeywords”inChapter12.Characteristicforthatonebeingaplug-inwasthemethodisResolverForKeyWord(),bywhichtheplug-incouldindicateforwhichkeyword,orkeywordpattern,itwouldoperate.

We’lltakealookatoneotherexamplehere,namely,theELresolver.ThepropertyresolverandvariableresolverarebothdeprecatedandhavebeenreplacedbytheELresolver.TheELresolveritselfisnotaJSF-specifictypebutoriginatesfromtheExpressionLanguage(EL)spec.Thisspecdoes,however,haveimportantpartsofitsoriginsinJSF.

TheELresolverallowsustointerprettheso-calledbaseandpropertyofanexpressioninacustomway.Consideringtheexpression#{foo.bar.kaz},then“foo”isthebaseand“bar”isthepropertywhenresolving“bar”,while“bar”isthebaseand“kaz”isthepropertywhenresolving“kaz”.Perhapssomewhatsurprisingatfirstisthatwhen“foo”isbeingresolved,thebaseisnulland“foo”isthe

Page 680: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

property.Inpractice,addingacustomELresolverisnotoften

needed,andwecanoftengetbywithsimplydefininganamedCDIbeanthatdoeswhatwerequire.CustomELresolverscouldcomeintoplaywhenJSFisintegratedinacompletelydifferentenvironmentthough,onewherewe’dlikeexpressionstoresolvetoacompletelydifferent(managed)beansystem.Evenso,aCDI-to-other-beans-bridgemightbeabetteroptioneventhere,butit’sperhapsgoodtoknowacustomELresolverisoneothertoolwehaveinourarsenal.

Anyway,todemonstratewhatanELresolvercandowe’llshowanexamplewherethebaseofanELexpressionisinterpretedasapattern,somethingwecan’tdodirectlywithanamedCDIbean.

ThefollowingshowsanexampleELresolver:

publicclassCustomELResolverextendsELResolver{

protectedbooleanisResolverFor(Objectbase,Object

property){

returnbase==null

&&propertyinstanceofString

&&((String)property).startsWith("dev");

}

@Override

publicObjectgetValue

(ELContextcontext,Objectbase,Objectproperty)

{

if(isResolverFor(base,property)){

context.setPropertyResolved(true);

returnproperty.toString().substring(3);

}

returnnull;

}

Page 681: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Override

publicClass<?>getType

(ELContextcontext,Objectbase,Objectproperty)

{

if(isResolverFor(base,property)){

context.setPropertyResolved(true);

returnString.class;

}

returnnull;

}

@Override

publicClass<?>getCommonPropertyType

(ELContextcontext,Objectbase)

{

returnbase==null?getType(context,base,null):

null;

}

@Override

publicbooleanisReadOnly

(ELContextcontext,Objectbase,Objectproperty)

{

returntrue;

}

@Override

publicvoidsetValue

(ELContextcontext,Objectbase,Objectproperty,

Objectvalue)

{

//NOOP;

}

@Override

publicIterator<FeatureDescriptor>getFeatureDescriptors

(ELContextcontext,Objectbase)

{

returnnull;

Page 682: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

}

}

IfwenowusethefollowingFacelet<!DOCTYPEhtml><htmllang="en"

xmlns="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

>

<h:head/>

<h:body>

<p>Test:#{devThisIsDev}</p>

</h:body>

</html>

we’llsee“Test:ThisIsDev”beingprintedwhenrequestingit.What’shappeninghereisthatthecustomELresolverhandleseverynamethatstartswith“dev.”

DynamicExtensionsThepreviousexamplesweremostlyaboutregisteringtheextensionsthatweneededstatically,e.g.,byregisteringafactoryorthetypedirectlyinfaces-config.xml.Factoriesgiveusanopportunityforsomedynamicbehavior.Thatis,atthepointthefactoryiscalledwecandecidewhatnewtype(ifany)toreturn.

Forevenmoredynamicbehaviorit’sfrequentlyrequiredtobeabletodynamicallyaddthefactories,orinCDItodynamicallyaddtheproducers.CDIhasanelaborateSPI(serverproviderinterface)forthis(simplycalled“CDIExtensions”)whichare,however,somewhatoutsidethescopeofthisbook.

Page 683: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Forclassicfactoriesandactuallyeverythingthat’sinfaces-config.xml,there’sasomewhatlow-levelmethodtoaddthesedynamically:theApplicationConfigurationPopulator,whichwe’lldiscussnext.

APPLICATIONCONFIGURATIONPOPULATORTheApplicationConfigurationPopulatorisamechanismtoprogrammaticallyprovideanadditionalfaces-config.xmlfile,albeitbyusingtheXMLDOM(DocumentObjectModel)API(applicationprogramminginterface).ThisDOMAPIcanbeslightlyobscuretouse,andthepoweroftheApplicationConfigurationPopulatorislimitedbyitsabilitytoonlyconfigure.There’snoSPIinJSFtodirectlymodifyotherfaces-config.xmlatthislevel.

Themechanismworksbyimplementingtheabstractclassjavax.faces.application.ApplicationConfigu

rationPopulatorandputtingthefullyqualifiedclassnameofthisinaMETA-INF/services/javax.faces.application.Appli

cationConfigurationPopulatorfileofaJARlibrary.

Todemonstratethis,we’llcreateanotherversionoftheExternalContextProducerthatwedemonstratedearlier,thistimeusingthementionedApplicationConfigurationPopulator.Forthiswetakethesamecode,removethefaces-config.xmlfile,andaddtheMETA-INF/servicesentryaswellasthefollowingJavaclass:

Page 684: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

publicclassConfigurationProvider

extendsApplicationConfigurationPopulator

{

@Override

publicvoidpopulateApplicationConfiguration(Document

document){

Stringns=

document.getDocumentElement().getNamespaceURI();

Elementfactory=document.createElementNS(ns,

"factory");

ElementexternalContextFactory=

document.createElementNS(ns,"external-context-

factory");

externalContextFactory.appendChild(

document.createTextNode(

ExternalContextProducer.class.getName()));

factory.appendChild(externalContextFactory);

document.getDocumentElement().appendChild(factory);

}

}

Acaveatisthatsincethisusesthejava.util.ServiceLoaderunderthehood,itreallyonlyworkswhenConfigurationProviderandExternalContextProducerarepackagedtogetherinanactualJARlibraryplacedintheWEB-INFliboftheWAR,insteadofjustbeingputdirectlyintheWAR.

THEAPPLICATIONMAINCLASSAbovewediscussedtheApplicationConfigurationPopulator,whichaswesawisactuallyafaces-config.xmlproviderofsorts.Thismeansitworkswithfullyqualifiedclassnamesandelementsthatarestilltextinnature.

JSFfeaturesavarietyofsomewhatmoretraditional

Page 685: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

programmaticAPIsaswell,withperhapsthemostwell-knownofthembeingthejavax.faces.application.Applicationmainclass,whichisamongothersaholderforthesamesingletonsthatwementionedpreviouslyinthesection“ExtendingClassicalArtifacts.”Forcompletenesswe’llrepeatthislisthere.

javax.faces.application.NavigationHandler

javax.faces.application.ViewHandler

javax.faces.application.ResourceHandler

javax.faces.component.search.SearchExpressionHandler

javax.faces.flow.FlowHandler

javax.faces.application.StateManager

javax.faces.event.ActionListener

AllofthesehavecorrespondingsettersontheApplicationclass.Forinstance,thefollowingshowstheJavadocandmethoddeclarationforActionListener:/***<p>

*Setthedefault{@linkActionListener}tobe

registeredforall

*{@linkjavax.faces.component.ActionSource}

components.

*</p>

*

*@paramlistenerThenewdefault{@link

ActionListener}

*

*@throwsNullPointerException

*if<code>listener</code>is

Page 686: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<code>null</code>

*/

publicabstractvoidsetActionListener(ActionListener

listener);

Likewise,theApplicationclassalsohas“add”methodsfortheplug-intypeswementionedinthesection“Plug-ins.”

javax.el.ELResolver

javax.faces.el.PropertyResolver(deprecated)

javax.faces.el.VariableResolver(deprecated)

javax.faces.component.serarch.SearchKeywordResolver

AdifficultywithusingtheApplicationclasstosetthesesingletonsis,firstofall,thatit’stimingsensitive.Thismeanswecanonlysetsuchclassesfromacertainpoint,whichisobviouslynotbeforethepointthattheApplicationitselfisavailable,andforsomesingletonsnotuntilthefirstrequestisserviced.Thisfirstrequestisasomewhatdifficultpointtotrack.

Specifically,theresourcehandler,viewhandler,flowhandler,andstatehandler,uhhh,statemanager,can’tbesetanymoreafterthefirstrequest,whiletheELresolverandsearchkeywordresolvercan’tbeaddedeitheraftersaidfirstrequest.

Todemonstratethiswe’lladdthecustomELresolveragainthatwedemonstratedabove,butinamoredynamicwaynow.Todothis,weremovetheELresolverfromourfaces-config.xmlfileandaddasystemlistenerinstead.Ourfaces-config.xmlfilethenlooksasfollows:<application>

<system-event-listener>

Page 687: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

<system-event-listener-class>

com.example.project.ELResolverInstaller

</system-event-listener-class>

<system-event-class>

javax.faces.event.PostConstructApplicationEv

ent

</system-event-class>

</system-event-listener>

</application>

Indeed,thisdoesn’tgetridoftheXMLand,infact,it’sevenmoreXML,butreducingorgettingridofXMLisnotthemainpointhere,whichistheabilitytoregistertheELresolverinamoredynamicway.

Thesystemeventlistenerthatwejustregisteredherelooksasfollows:

publicclassELResolverInstallerimplements

SystemEventListener{

@Override

publicbooleanisListenerForSource(Objectsource){

returnsourceinstanceofApplication;

}

@Override

publicvoidprocessEvent(SystemEventevent){

Applicationapplication=(Application)

event.getSource();

application.addELResolver(newCustomELResolver());

}

}

Whatwehavehereisasystemeventlistenerthatlistenstothe

Page 688: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

PostConstructApplicationEvent.Thisisgenerallyagoodmomenttoaddplug-insliketheELresolver.TheApplicationinstanceisguaranteedtobeavailableatthispoint,andrequestprocessinghasn’tstartedyet,sowe’resurelyintimebeforethefirstrequesthasbeenhandled.

LocalExtensionandWrappingInsomecases,wedon’twanttooverride,say,aviewhandlerglobally,butonlyforalocalinvocationof,typically,amethodinacomponent.JSFgathersforthisbypassingontheFacesContext,whichcomponentstouseasthemainentrypointfromwhichtogetprettymuchallotherthings.ComponentsareforJSF2.3notCDIartifactsorotherwiseinjectable,sotheCDIapproachforthemomentdoesn’tholdforthem.

Localextensioncanthenbedonebywrappingthefacescontextandpassingthatwrappedcontexttothenextlayer.NotethattheServletspecusesthesamepatternwheretheHttpServletRequestandHttpServletResponsecanbewrappedbyafilterandpassedontothenextfilter,whichcanwrapitagainandpassittoitsnextfilter,etc.

Toillustratethis,supposethatforacertaincomponentwe’dliketoaugmenttheactionURLgenerationusedby,amongothers,formcomponentsinsuchawaythat“?foo=bar”isaddedtothisURL.IfwedothisbygloballyoverridingtheviewhandlerallcomponentsandothercodeusingtheviewhandlerwouldgettoseethisURL,whileherewe’donlywantthisforthisveryspecificcomponent.

Toachievejustthis,weusewrappinghereasillustratedinthefollowingcomponent:

Page 689: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@FacesComponent(createTag=true)

publicclassCustomFormextendsHtmlForm{

@Override

publicvoidencodeBegin(FacesContextcontext)throws

IOException{

super.encodeBegin(newActionURLDecorator(context));

}

}

ImplementingthewrapperwithoutsupportfromJSFwouldbeasomewhattedioustask,tosaytheleast,asthepathfromFacesContexttoViewHandlerisafewcallsdeepandtheclassesinvolvedhavealargeamountofmethods.LuckilyJSFgreatlyeasesthistaskbyprovidingwrappersformostofitsimportantartifacts,withaneasy-to-useconstructor.

Thepatternusedhereisthatthetop-levelclass(ActionURLDecoratorfromtheexampleabove)inheritsfromFacesContextWrapperandpushestheoriginalfacescontexttoitssuperclass.Thenthepathofintermediateobjectsisimplementedbyoverridingtheinitialmethodinthechain(getApplication()here),andreturningawrapperforthereturntypeofthatmethod,withthesuperversionofitpassedintoitsconstructor.Thiswrapperthendoesthesamethingforthenextmethodinthechain,untilthefinalmethodisreachedforwhichcustombehaviorisrequired.

Thefollowinggivesanexampleofthis:publicclassActionURLDecoratorextendsFacesContextWrapper{

publicActionURLDecorator(FacesContextcontext){

super(context);

}

Page 690: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

@Override

publicApplicationgetApplication(){

returnnew

ApplicationWrapper(super.getApplication()){

@Override

publicViewHandlergetViewHandler(){

returnnew

ViewHandlerWrapper(super.getViewHandler()){

@Override

publicStringgetActionURL

(FacesContextcontext,String

viewId)

{

Stringurl=

super.getActionURL(context,viewId);

returnurl+"?foo=bar";

}

};

}

};

}

}

WiththesetwoclassespresentintheJSFapplication,nowconsiderthefollowingFaceletthatmakesuseofourcustomformcomponent:

<!DOCTYPEhtml>

<htmllang="en"

xmlns="http://www.w3.org/1999/xhtml"

xmlns:h="http://xmlns.jcp.org/jsf/html"

Page 691: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

xmlns:test="http://xmlns.jcp.org/jsf/component"

>

<h:head/>

<h:body>

<test:customFormaction="index">

<h:commandButtonaction="index"value="GoIndex"

/>

</test:customForm>

</h:body>

</html>

RememberthatnoXMLregistrationisneededforthecustomcomponentwhenit’sannotatedwith@FacesComponent(createTag=true),andthatitsXMLnamespacedefaultstohttp://xmlns.jcp.org/jsf/componentanditscomponenttagnametothesimpleclassname.(SeealsoChapter11.)IfwerequesttheviewcorrespondingtothisFaceletandpressthebutton,we’dindeedsee“?foo=bar”appearingaftertheURL,meaningthatourlocalextensionoftheviewhandlerviaachainofwrappershasworkedcorrectly.

IntrospectionAnimportantaspectofbeingabletoextendaframeworkcorrectlyisnotonlytobeabletoutilizeextensionpointsbutalsotobeabletointrospecttheframeworkandqueryitforwhatartifactsorresourcesithasavailable.

OneoftheplaceswhereJSFprovidessupportforintrospectionisitsabilitytorevealwhichviewresourcesarepresentinthesystem.RememberthatinJSF,viewslike,forexample,Faceletsareabstractedbehindtheviewhandler,whichinturnmanagesoneormoreviewdeclarationlanguage

Page 692: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

(VDL)instances.AVDL,alsocalledatemplatingengine,hastheabilitytoreaditsviewsviatheresourcehandler.Aswe’veseeninthischapter,allthesethingscanbeaugmentedorevenfullyreplaced.

Thisspecificallymeansthatviewscancomefromanywhere(e.g.,fromthefilesystem(mosttypical))butcanalsobegeneratedinmemory,beloadedfromadatabaseorfetchedoverthenetwork,andmuchmore.Also,thesimplephysicalfiletologicallyviewnamemappingsuchasthatusedbyFaceletsdoesn’thavetoholdforotherviewdeclarationlanguagesatall.

Togetherthismeansthatwithoutanexplicitintrospectionmechanismwheretheviewhandler,VDL,andresourcehandlercanbeaskedwhichviews/resourcestheyhaveavailable,wewouldnotbeabletoreliablyobtainafulllistofviews.

Suchlistofviewsisneeded,forexample,whenwewanttoutilizeso-calledextensionlessURLs,whichareURLswithoutanyextensionsuchas.xhtmlor.jsf,andwithoutanyextrapathmappingpresentsuchasfaces*.LackinganyhintinsidetheURLitself,theServletcontainerontopofwhichJSFworkshastohavesomeotherwayofknowingthatacertainrequesthastoberoutedtothefacesservlet.

AparticularlyelegantwaytodothisisbyutilizingServlet’s“exactmapping”feature,whichisavariantofURLmappingwhereanexactnameinsteadofapatternismappedtoagivenservlet,whichinthiscasewouldbethefacesservlet.SincetheServletspechasanAPIfordynamicallyaddingServletmappings,andJSFhasanAPItodynamicalintrospectwhichviewsareavailable,weprettymuchonly

Page 693: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

havetocombinethesetwotoimplementextensionlessURLs.Thefollowingshowsanexampleofhowtodothis:

@WebListener

publicclassExtensionLessURLsimplements

ServletContextListener{

@Override

publicvoidcontextInitialized(ServletContextEvent

event){

FacesContextfacesContext=

FacesContext.getCurrentInstance();

event.getServletContext()

.getServletRegistrations()

.values()

.stream()

.filter(servlet->servlet

.getClassName().equals(FacesServlet.clas

s.getName()))

.findAny()

.ifPresent(facesServlet->facesContext

.getApplication()

.getViewHandler()

.getViews(facesContext,"/",

ViewVisitOption.RETURN_AS_MINIMAL_IM

PLICIT_OUTCOME)

.forEach(view->

facesServlet.addMapping(view)));

}

}

Whathappenshereisthatwefirsttrytofindthefacesservlet,whichincidentallyisanotherexampleofintrospection,this

Page 694: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

timeintheServletspec.Iffound,weasktheviewhandlerforallviews.Asmentionedabove,thiswillinternallyintrospectalltheavailableviewdeclarationinstances,whichinturnmayintrospecttheresourcehandler.The“RETURN_AS_MINIMAL_IMPLICIT_OUTCOME”parameterisusedtomakesureallviewsarereturnedintheirminimalformwithoutanyfileextensionsorothermarkersappended.Thisisthesameformthatcanbereturnedfrom,forexample,actionmethodsorusedwiththeactionattributeofcommandcomponents.

Havingobtainedthestreamofviewsintherightformat,wedirectlyaddeachofthemasexactmappingtothefacesservletthatwefoundearlier.

Forexample,supposewehaveaFaceletviewinthewebrootinafolder/foonamedbar.xhtml.ThengetViews()willreturnastreamwiththestring“foobar”.Whenthefacesservletismappedtothis“foobar”,andassumingtheJSFapplicationisdeployedtothecontextpath/testondomainlocalhostport8080,wecanrequesthttp://localhost:8080/testfoobartoseetherenderedresponseofthatFacelet.

Notethateventhoughit’srelativelysimpletoachieveextensionlessURLsinJSFthisway,it’sstillsomewhattedioustohavetodothisforeveryapplication.It’sexpectedthatanextrevisionofJSFwillsupportthisviaasinglesetting.

Page 695: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

Index

A1. Actionlistenermethod2. ADFFaces3. Ajax

1. action2. applyrequestvaluesphase3. behaviorlistenermethods4. ClientBehaviorHolder5. executeandrenderattributes6. <f:ajax>tag7. @formkeyword8. <h:form>9. implementingclass10. invokeapplicationphase11. javax.faces.ViewState12. JSFlifecycle13. lifecycle14. messagecomponent15. navigationin16. on[event]attributes17. renderresponsephase18. supportingeventtypes19. valueChange

4. Ajaxexceptionhandling

1. Ajaxrequest

Page 696: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

2. application-widecustomizations3. businesslogic4. ExceptionHandler5. handleAjaxException()6. HTTPresponse7. JavaScriptalert8. Productionstage9. UIViewRootinstance10. unhandledexceptionevents

5. AlexanderSmirnov’sTelamonframework6. Applicationprogramminginterface(API)7. Applicationservers8. Authenticationmechanism

1. AuthenticationMechanismDefinitionannotation2. caller-initiatedauthentication

See Caller-initiatedauthentication

1. CustomFORM2. @CustomFormAuthenticationMechanismDefinitionannotation3. @FacesConfigannotation4. FORM5. customJSFcode

1. actionmethod2. backingbean3. continueAuthentication()method4. loginpage

6. loginToContinueattribute

B1. Backingbeans

Page 697: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

1. class

1. editing2. JBossToolsplug-in

2. JSFvendedtypes,injecting3. layers4. managedbean

See Managedbeans

1. MVCframework2. namingconventions

1. BeanManager#fireEvent()method2. BeanValidationAPI

1. commaseparatedstring2. contextparameter3. customconstraintannotation4. custommessages5. groupsattribute6. inJavacode7. JPA-managed8. NotNull.class9. UIInputcomponents10. validationerror11. validationGroupsattribute12. web.xml

3. Bindingattribute4. Byrolesecurityconstraints

C1. Cachebusting

Page 698: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

2. Caller-initiatedauthentication

1. continueAuthentication()method2. Flash3. login-to-continueservice4. SecurityContext#authenticate()method5. SUCCESScase

3. CDI-centricapproach

1. backingbean2. getPreviousMap()method3. RequestParameterMapProducerclass

4. Classicalapproach

1. CDIversion2. externalcontextfactory3. Facelet4. factories5. replace/augment

5. Commandcomponents

1. actionlistenermethod2. argumentpassingapproach3. bindingattribute4. FontAwesomeicon5. JavaScriptwithhard-codedvariables6. loadsandrendersdatatable7. managedbeanactionmethod8. Mojarraspecificcase9. namespacedfunction10. paramsproperty11. passadditionalrequest12. targetmethod13. UXconsensus

6. Componentsystemevents

Page 699: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

1. javax.faces.event.ComponentSystemEventabstractclass2. javax.faces.event.ComponentSystemEventListenerinterface3. onload()method4. @PostConstruct-likebehavior5. PostConstructViewMapEvent6. PreDestroyViewMapEvent7. PreInvokeApplicationEvent8. Rendererclass9. subscribelisteners10. UIComponent#subscribeToEvent()11. YourComponent12. YourListenerclass

7. Componenttree

1. coretags2. #{dynamicForm}3. HTML

See HTMLcomponents

1. HTTPrequest2. JSTLCoreTags3. lifecycle

1. invokeapplicationphase2. processvalidationsphase3. requestvaluesphase4. restoreviewphase

4. modelvaluesphase5. phaseevents6. PostAddToViewEvent7. PreRenderViewEvent8. renderresponsephase9. steps10. TextField#populate()11. XML

Page 700: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

1. Compositecomponents

1. Ajaxlistener2. backingbean3. backingcomponent4. caveat5. clientID6. defaultevent7. encodeBegin()method8. eventattribute9. getSubmittedValue()method10. implementation11. interface12. JSF2.013. JSFphases14. LocalDateproperty15. NamingContainer16. recursive17. space-separatedcollection18. UIComponentinstance19. UIInputsuperclass20. XMLnamespace

2. Container-initiatedauthentication3. Contextpath4. Converters

1. beanproperty2. custom

1. baseentityservice2. converterIdattribute3. emptystring4. equals()andhashCode()methods5. forClassconverterforjava.lang.String6. genericconverter7. getId()8. GETrequestparameter9. LocalDateConverter10. managedattribute

Page 701: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

11. NumberConverter12. Object#equals()method13. plainJavacode14. ProductConverter15. Productentity16. productID17. usecase18. webapplication

3. EL4. <f:convertDateTime>

1. backingbean2. Chromebrowser3. HTML5dateandtimeinputs4. ISO8601format5. supportedvalues

5. <f:convertNumber>

1. currencysignpattern2. NumberFormatinstance3. standardnumberformatpattern4. tags

6. interface7. ValueHoldercomponents

5. Coretags

1. JSTL

See JSTLCoreTags

1. standardize

1. Cross-siterequestforgery(CSRF)

Page 702: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

1. bankURL2. bar.xhtml3. faces-config.xml4. HTTPheaders5. maliciousrequests6. protection

2. Cross-sitescripting(XSS)

1. backingbean2. commoncontext3. Faceletand<h:outputText>component4. ServletContextListener5. sessionIDcookie6. URLs

3. Customcomponents

1. componentfamily2. componenttype3. customtaghandlers4. distributableJAR,packaging5. existingcomponent,extending6. existingrenderer,extending7. newcomponentandrenderer

1. backwardcompatibility2. DataModelabstractclass3. EL4. encodeBegin()method5. encodeChildren()method6. encodeEnd()method7. @FacesComponent8. @FacesDataModelannotation9. officialcomponentattribute10. UIComponentsubclass11. UIDatasubclass12. valueattribute13. XMLnamespace

Page 703: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

8. renderertype9. resourcedependencies

D1. Databaseidentitystore2. Datasource3. Doublesubmit

E1. EarlyAccessSpecification2. Eclipse

1. configuresettings2. Faceletsfile3. GlassFish

1. location2. Payaraservername3. selecttools

4. install5. installJBossTools6. JavaEEAPI7. Mavenproject

1. createsimpleproject2. GAVin3. JavaEE84. JBossToolsplug-in5. JPAfacetconfiguration6. JSFcapabilitiesconfiguration7. Markersview

Page 704: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

8. ModifyFacetedProjectwizard9. Oxygen210. pom.xmlfile11. ProjectExplorerview12. selecting13. ServletAPI14. settings15. yellowwarningbar

8. PATH9. ProjectExplorerview10. serversviewof11. workbench

3. EJBExceptionhandling4. E-mailaddresscolumn5. E-mail-basedsignup6. EnterpriseJavaBeans(EJB)7. Exadel8. ExadelVisualComponentPlatform9. ExpressionLanguage(EL)10. Extensions

1. ApplicationConfigurationPopulator2. applicationmainclass

1. ActionListener2. eventlistener3. faces-config.xml4. plug-ins

3. CDI-centricapproach

See CDI-centricapproach

1. classicalapproach

See Classicalapproach

Page 705: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

1. customformcomponent2. dynamic3. introspection

1. extensionlessURLs2. VDL

4. localextension5. plug-ins

1. ELresolver2. factory-liketypes

6. wrapping

1. ExternalContext#encodeWebsocketURL()method

F1. Facelets2. FaceletsfitsJSFlikeaglove(article)3. Faceletstemplating

1. compositecomponents

See Compositecomponents

1. implicitELobjects2. SPA3. tagfiles

1. boilerplatecode2. client3. customizing4. duplicatecomponentID5. HTML5inputfields

Page 706: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

6. implementation7. insideLabel8. mastertemplatefiles9. <method-signature>10. <required>property11. taglibfile12. <ui:include>13. view-scopedmodel

4. templatecompositions

1. client2. compiler3. finalHTMLoutput4. getContextPath()method5. mastertemplatefile6. xmlnsattribute

5. templatedecorations6. XHTML

1. HTML52. JurassicIE63. SAXparser4. webdevelopers

1. FacesServlet2. File-basedinputcomponent3. FORMauthenticationmechanism

G1. GETforms

1. <f:metadata>2. invokeapplicationphase

Page 707: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

3. requestparametermap4. searchform5. submittedvalue6. UIViewAction7. UIViewParameter8. usingtemplating

2. GlassFish

1. location2. Payaraservername3. selecttools

H1. H2database

1. configuredatasource2. createEJBservice3. HelloWorld

1. gettersandsetters2. savemessage

4. JPA

1. configuration2. createentity

5. pom.xml

2. HelloWorld

1. <application>element2. bracenotation3. changeactivelocale

Page 708: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

4. commonkeys5. commonprefix6. config.xmlfile7. createbackingbeanclass8. Faceletsfile9. Faceletstemplate10. gettersandsetters11. HTTPAccept-Languageheader12. labelattribute13. langattribute14. localeproperty15. localizeenums16. mapkeys17. <message-bundle>entry18. newmessage19. overridedefaultmessage20. page-specificentries21. parametersresource22. Payaraserver

1. AddandRemovewizard2. automaticpublish3. inChromebrowser4. contextpath5. root6. startserver7. web.xml

23. ResourceBundle

1. database-based2. HTMLin

24. savemessage25. XHTMLtags

3. Hiddeninputfield4. HTMLcomponents

Page 709: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

1. input2. JSFpage3. output4. Sincecolumn5. tag6. UIComponentsuperclass7. valuetypecolumn8. Viewpagesource

I1. ICEbrowserbeans2. ICEsoft3. Identitystore

1. application-specificuserdata2. authenticationmechanism3. database4. groupsandroles5. SQLstatement

4. Immediateattribute5. Implicitnavigation6. ImprovingJSFbyDumpingJSP(article)7. Inputcomponents

1. file-based

See File-basedinputcomponent

1. HTML2. text-based

See Text-basedinputcomponents

1. InternetAssignedNumbersAuthority(IANA)2. Invokeapplicationphase

Page 710: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

3. IOExceptionhandling

J,K1. JACC2. JASPIC3. JavaEE84. JavaEEAPI5. JavaEESecurity

1. API2. customprincipal3. excludedconstraints4. identitystore

See Identitystore

1. JACC2. JASPIC3. logout4. remembermeservice5. renderedattribute

1. finer-grainedname2. hasAccessToWebResource()3. implicitobject#{request}4. “**”role5. SecurityContext6. utilitymethods7. web.xml

6. byroleconstraints7. Servletspec8. sourceexposureprotection

1. compositecomponents2. FaceletsandJSPfiles

Page 711: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

3. FacesServlet4. frontcontroller5. preventionmethods6. *.xhtml

9. SPIs10. uncheckedconstraints

1. JavaNamingandDirectoryInterface(JNDI)2. JavaOne20153. JavaPersistenceAPI(JPA)

1. configuration2. createentity3. entities4. facetconfiguration5. implementation

4. JavaSEJDK5. JavaServerFaces(JSF)

1. ApacheStruts2. conflicts3. developer4. 1.2EG5. ExpressionLanguage6. ICEsoft7. managedbeans8. OmniFaceslibrary9. OurFaces10. PrimeFaces11. viewhandler

6. JavaServerPages(JSP)7. JBossTools8. JSF2.0

1. CDIspec2. Facelets

Page 712: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

3. goals4. javax.faces.bean.RequestScopedannotation5. PrimeFaces6. TheTrap7. viewscope

9. JSF2.210. JSF2.311. JSPStandardTagLibrary(JSTL)12. JSR-12713. JSTLCoreTags

1. backingbeanproperty2. calculatesuminloop3. componenttree4. ELscope5. Facelets6. IllegalStateException7. input.xhtmltagfile8. itementity9. namespaceURI10. specification11. viewbuildtime12. viewrendertime

L1. Labelcomponent

M1. Managedbeans

1. CDI

Page 713: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

2. eagerinitialization3. ELcontext4. initializationanddestruction5. JavaEE66. JSF2.07. JSFdeveloper8. scopes

1. @ApplicationScoped2. tochoose3. @ConversationScoped4. @Dependent5. @FlashScoped6. @FlowScoped7. @RequestScoped8. @SessionScoped9. @ViewScoped

2. Messagecomponents3. Message-drivenbean(MDB)4. Modelentity5. Modelvaluesphase6. Model-view-controller(MVC)7. Mojarra

1. <f:websocket>2. specificcase

8. MyFaces

N1. Navigation

Page 714: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

O1. OmniFaceslibrary2. Opensourceimplementations3. OurFaceslibrary4. Outputcomponents

1. dataiterationcomponent

1. add/removerows2. dynamiccolumns3. editable4. ELvalueexpression5. listofproducts6. Productentity7. selectrowsin<h:dataTable>8. valueattributeofUIData9. varattribute

2. document-based3. navigation-based4. panel-based

1. <h:panelGrid>2. inlineelements3. iterationindex4. JSFcomponent5. JSTL6. <ui:instructions>7. userprofile

5. pass-throughelements6. resourcecomponents

See Resourcecomponents

1. text-based

Page 715: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

1. Facelets2. <h:outputText>3. JSF4. malicioususer5. managedbeanproperty6. Markdowninterface7. MarkdownListenerentitylistener8. Messageentity9. predefinedhuman-friendlymarkupformat

1. Oxygen2

P,Q1. Pass-throughelement2. PatentpendingDirect-to-DOM™3. Payara

1. GlassFish2. installing3. JAVA_HOME4. version

4. @Phoneconstraint,custom5. Portlet-basedwebapplications6. Post-Redirect-Getpattern7. PrimeFaceslibrary8. Processvalidationsphase9. PushContextinterface

R1. Referenceimplementation(RI)2. Remembermeservice

Page 716: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

1. authenticationmechanism2. CustomFormAuthenticationMechanism3. HttpAuthenticationMechanismWrapper4. @RememberMeannotation5. token

3. Renderedattribute

1. finer-grainedname2. hasAccessToWebResource()3. implicitobject#{request}4. “**”role5. SecurityContext6. utilitymethods7. web.xml

4. Renderresponsephase5. Requestscope6. Requestvaluesphase7. Resourcecomponents

1. advantages2. bsf3. cachebusting4. classpath5. createResource()method6. DynamicResourceListener7. FacesServlet8. <h:outputScript>9. JSFcomponents10. JSF2.3webapplication11. nameattribute12. OmniFaces13. physicalresourcefiles14. PrimeFaces15. rendererclass16. renderingorder17. @ResourceDependencyannotation18. ResourceHandler19. SystemEventListener

Page 717: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

20. webapplication

8. Restoreviewphase9. RichFaces

S1. Searchexpressions

1. absolutehierarchicalIDs2. relativelocalIDs3. searchkeywords

See Searchkeywords

1. Searchform2. Searchkeywords

1. custom2. standard

3. Selectioncomponents

1. demonstratedapproach2. HTMLmarkup3. JSF2.24. JSF2.35. newgroupattribute6. UISelectBooleancomponent7. UISelectManycomponent

4. SelectItemtags

1. countrymodelentities2. ELproperties3. <f:selectItem>

Page 718: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

4. <h:selectOneMenu>5. itemLabelattribute6. itemValueattribute7. mapkey8. noSelectionOptionattribute9. SelectItemGroup

5. Serverproviderinterfaces(SPIs)6. Servletcontainers7. Servletspec8. Sessionscope9. SinglePageApplication(SPA)10. SkinServlet11. someLongRunningProcess()method12. Soteria13. Spring1.0.MyFaces14. Statelessforms15. Strutswebframework

T1. Text-basedinputcomponents

1. basicusageexample2. Chrome3. hiddeninputfield4. idattribute5. invokeapplicationphase6. Portlet-basedwebapplications7. processvalidationsphase8. UIInput#decode()method9. UIInput#updateModel()method10. UIInput#validate()method

2. TheTrap

Page 719: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

U1. UIInputsuperclass2. UnifiedExpressionLanguage(UEL)3. Userinterface(UI)4. UserInterfaceXML(UIX)

V1. Validators

1. BeanValidationAPI

1. commaseparatedstring2. contextparameter3. customconstraintannotation4. groupsattribute5. inJavacode6. JPA-managed7. NotNull.class8. UIInputcomponents9. validationerror10. validationGroupsattribute11. web.xml

2. convertsubmittedvaluetostring3. e-mail-basedsignup4. minimumandmaximumattributes5. providedbyJSF6. requiredattribute

2. Viewbuildtime3. Viewdeclarationlanguage(VDL)4. ViewExpiredExceptionhandling5. ViewHandler#getWebsocketURL()method

Page 720: The Definitive Guide to JSF in Java EE 8: Building Web Applications with JavaServer Faces

6. Viewrendertime7. Viewscope8. Viewstate

W1. Webfragment2. Webparametertampering3. WebSocketpush

1. channeldesignhints2. configuration3. f:websocketimplementation4. one-timepush5. scopesandusers6. sessionandviewexpiration,detecting7. site-widepushnotifications8. statefulUIupdates9. trackofactivesockets10. usage

X,Y,Z1. XHTML2. XSS

See Cross-sitescripting(XSS