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

Post on 11-Sep-2021

19 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

BaukeScholtzand

ArjanTijms

TheDefinitiveGuidetoJSFinJavaEE8BuildingWebApplicationswithJavaServerFaces

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.

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,e-mailorders-ny@springer-sbm.com,orvisitwww.springeronline.com.ApressMedia,LLCisaCaliforniaLLCandthesolemember(owner)isSpringerScience+BusinessMediaFinanceInc(SSBMFinanceInc).SSBMFinanceIncisaDelawarecorporation.

Tocaffeineandour(notso)patientwives.

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

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

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>

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

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

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

15. Chapter15:Extensions

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

1. ApplicationConfigurationPopulator2. TheApplicationMainClass

6. LocalExtensionandWrapping7. Introspection

16. Index

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

mostfrequentlyaskedquestionsandcorrectlysolvemostcommonlyencounteredproblemswhileusingJSF.

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

AbouttheTechnicalReviewer

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.

(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

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

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).

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

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

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

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”

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

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

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

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.

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

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

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

OnDecember23,CayHorstmannraisedhisconcernsaboutthisveryunwantedsituationinanarticletitled“Is@javax.faces.bean.ManagedBeanDeadonArrival?”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

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

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.

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,

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

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

(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

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

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

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.

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

that,youwillentertheworkbench.Bydefault,itlookslikethescreenshotinFigure2-1.

Figure2-1 Eclipseworkbench

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

General➤Workspace➤TextfileencodingmustbesettoUTF-8.ParticularlyinWindowsthismightotherwisedefaulttotheproprietary

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

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).

Figure2-3 SelectonlytheCDItoolsfornow

Next,acceptthetermsofthelicenseagreementandcompletethewizarduntilEclipseisrestarted.

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

InordertointegrateanewapplicationserverinEclipse,firstcheckthebottomsectionoftheworkbenchwithseveraltabsrepresentingseveralViews(youcanaddnewonesvia

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

Figure2-4 ServersviewofEclipseWorkbench

Fromthelistofavailableservertools,selectOracle➤GlassFishTools(seeFigure2-5).

Figure2-5 SelectingGlassFishToolsinNewServerwizard

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

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

Figure2-6 SelectingGlassFishserverinNewServerwizardandnamingitPayara

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

Figure2-7 SpecifyingGlassFishlocationinNewServerwizard

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

Figure2-8 ThePayaraserverinServersview

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

Projectwizardwhichmayhaveabittoomanyoptions.Eclipse,beinganIDEformanydifferentprojecttasks,

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

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

Figure2-9 SelectingMavenProjectinNewProjectwizard(notetheDynamicWebProjectasanotherbutnon-viableoption)

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.

Figure2-11 FillingouttheMavenGAVinnewMavenProjectwizard

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

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.

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>

</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.

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

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

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

Figure2-14 TheJPAFacetconfiguration

Notethatconfiguringadatabaseconnectionisnotnecessaryfornowaswe’regoingtouseanembeddeddatabase.

InthenextstepoftheModifyFacetedProjectwizard,wecanconfiguretheJSFcapabilities(seeFigure2-15).Alsohere,

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

Figure2-15 TheJSFCapabilitiesconfiguration

Actually,theentireregistrationoftheFacesServletin

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.

Figure2-16 CorrectlyconfiguredJavaEE8MavenprojectinEclipse

WeonlyneedtoadjustallthedeploymentdescriptorstocatchuptotheactuallyusedServlet,JSF,JPA,andCDIversions.ThisisnormallydonebyadjustingtherootelementofthedeploymentdescriptorXMLfiletosetthedesiredXMLschemasandtheversion.

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

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.-->

</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>

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.

Figure2-17 TheJBossTools-providedNewCDIBeanwizardinEclipse

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

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.

@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.

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"

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>.

<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.

Waituntiltheserverstartsupandhas,intheServersview,gainedthestatusStarted(seeFigure2-19).

Figure2-19 ThePayaraserverinServersviewwiththestatusStarted(notethattheConsoleviewishighlightedasithasunreadserverlogs)

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

Figure2-20 TheAddandRemovewizardwhereintheprojecthasbeendeployedtotheserverbymovingittotheright

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

Now,openatabinyourfavoritewebbrowser(seeFigure2-21)andentertheaddress

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

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).

Figure2-22 PayaraserverconfigurationinEclipsewithautomaticpublishingenabledandintervalsetto0seconds

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

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

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.

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

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).

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{

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

@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;

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@Statefuland@Singleton.Notethata@Statelessdoesnotmeanthatthecontainerwillmakesurethattheclassitselfisstateless.Youasdeveloperarestillresponsibletoensurethattheclassdoesn’tcontainanysharedandmutableinstancevariables.Otherwise,you’dbettermarkitaseither@Statefulor@Singleton,dependingonitspurpose.

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

ADJUSTINGTHEHELLOWORLDNowwe’regoingtoadjusttheearliercreatedHelloWorldbackingbeaninordertosavethemessagesinthedatabaseand

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"/>

<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

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.

(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

Library)canbeusedtomanipulatethecomponenttreeusingjustXML.

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

Figure3-1 HowJSFprocessestheHTTPpostbackrequestwithintheMVCarchitecture(thenumbersrepresenttheordering)

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-

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

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>

...

</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

<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.

>

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

<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

<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

<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

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.

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

<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

<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

<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

<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

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

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

(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()

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

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.

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

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

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

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.

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

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

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

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.

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

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,

andtheapplicationdoesn’tperformanykindofnavigation(i.e.,theactionmethodreturnsnullorvoid),thentheviewstateidentifierwillstaythesameandtheviewscopewillbeprolongedtothenextpostbackrequest,untiltheapplicationperformsanexplicitnavigation,orwhentheHTTPsessionexpires.YoucanestablishtheviewscopebystoringtheobjectofinterestasanentryofUIViewRoot#getViewMap().ThisisexactlywhereJSFstoresits@ViewScopedmanagedbeans.No,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

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

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

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

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

<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>

<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);

}

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();

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

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:

@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

<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

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

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>

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.

<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}"/>

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:

<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.

<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}"/>

#{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

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

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;

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()+"']}"

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']}"

/>

<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());

}

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.

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.

(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

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.”

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{

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>

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,

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"

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

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

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

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);

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

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

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());

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

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

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}">

<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);

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"

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"/>

</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">

<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(){

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>

<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

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

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>

<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

<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}"

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

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;

@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"

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();

}

//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

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

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

The<h:selectManyCheckboxlayout="pageDirection">willrenderitasanestedtable:

Notetheimportanceofthelayout="pageDirection"attributein<h:selectOneRadio>and<h:selectManyCheckbox>.Thiswilllookmuchbetterthanthedefaultoflayout="lineDirection"whichwouldrendereverythinginabigsingletablerow.

LabelandMessageComponents

table:

The<h:selectManyListbox>willrendereachgroupas

HTML<optgroup>:

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"/>

<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>

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

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.

<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.

@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{

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

<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:

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"

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

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

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-

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:"john.doe@example.com"

};

invokeBeanSubmit(params);

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>

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

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(){

//...

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

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

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

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"/>

<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

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

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,and@none.Asitsnamesuggests,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();

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){

//...

}

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"/>

</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

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

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"

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

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>

<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.

}

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

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

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-.

(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

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

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

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

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

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.

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

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

<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

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

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

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

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

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

<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>

<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:"+

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

<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

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>

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>

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"/>

</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

<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>

...

<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

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

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

@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

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

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:

@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>

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

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"

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

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>

<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

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

}

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

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

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

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

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

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);

}

}

@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

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.

@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)

{

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.

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();

}

}

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

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>

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

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

Finally,youmightalsowanttoconsiderinvalidatingordeduplicatinge-mailsthatcontainthe“+”characterintheusernamepart,followedbyasequenceofcharacters,representingane-mailalias.Foralotofe-mailproviders,notablyGmail,e-mailaddressesfoo@gmail.comandfoo+bar@gmail.comrefertoexactlythesamee-mailaccount,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

@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){

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

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">

</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

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

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-

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.

(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

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

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

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

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

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

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"));

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.

}

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>

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

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

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

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}"/>

#{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

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?

<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

</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

“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

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

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;

}

}

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

<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.

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();

}

}

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

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

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

<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

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"

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

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

{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

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>

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

@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>

</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

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

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>

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,

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>

</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,

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

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

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.

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);

}

@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

</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.

<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>

<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>

<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;

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");

}

}

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"

/>

<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

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

<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">...

<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>

<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>

<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>

<*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.

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.

17http://omnifaces.org.

18http://bootsfaces.net.

19https://caniuse.com/#feat=http2.

(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

<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

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

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

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

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>

<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

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>

</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-

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.

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

<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="#

{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

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;

}

}

Thedefaultpageisdefinedin@PostConstruct.Otherwise,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=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.

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:info@example.com"

title="Email">info@example.com</a>

</li>

</ul>

</nav>

</section>

</ui:composition>

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>

<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}"/>

<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>

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>

<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>

<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

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"

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}">

<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()}">.

<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>

<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">

<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

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

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.

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.

<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.">

</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

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"/>

</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()

.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()||

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;}

}

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

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"

/>

<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

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

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>

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

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

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}"/>

</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().

<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;

@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>

</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

#{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

{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

#{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

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'};

}

...

.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

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..

(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

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.

Figure8-1 ThepositionofthebackingbeaninJSF’sMVCparadigm

InthisMVCparadigmthebackingbeanhasthusaratheruniqueposition.Notethatthebackingbeandoesn’tnecessarilyneedtoberepresentedbyasingleclass.Itcanevenberepresentedbymultipleclasses,eachwithitsownmanagedbeanscope,liketheviewcanberepresentedbymultipleFaceletsfilesandthemodelcanberepresentedbymultipleEJB/JPAclasses.

ComingbacktotheJSFdeveloper’spointofview,wecanevengetastepfurtherwithconsideringwhetherthebackingbeanisamodeloracontroller,dependingonhowyoucodethebackingbeanclass.Followingisoneway:@Named@RequestScoped@Stateful

publicclassProductBacking{

privateStringproductName;

privateStringproductDescription;

@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{

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();

}

}

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

managementfacility,suchasCDI.Thebeanmanagementfacilitywillautomaticallymanagethebean’slifecyclebyperformingconstruction,dependencyinjection,anddestructionwhennecessary,withoutyouhavingtodoitmanually.Ifyou’veeverdevelopedwithJSP/Servlets,thisbasicallyremovestheneedtomanuallyinstantiatebeansandputthemasanattributeoftheServletContext,HttpSession,orServletRequest. ToregisterabackingbeanclassasaCDImanagedbeanforJSFviews,simplyputthe@javax.inject.Namedannotation ontheclasssignature.

@Named

publicclassBackingBeanClass{

//...

}

ItwillthenimmediatelybeavailableinELcontextby#{backingBeanClass}andinallothermanagedbeansvia@Inject.TheELcontextisdirectlyavailableinFaceletsfiles.Bydefault,themanagedbeannameisderivedfromthebackingbean’sclassnamebylowercasingthefirstcharacter.Thiscanoptionallybeoverriddenbyspecifyingthevalueofthe@Namedannotation.

@Named("managedBeanName")

publicclassBackingBeanClass{

//...

}

ThisisnowinELcontextavailableby#{managedBeanName}.Nothinghaschangedforthe

2

3

@Injectapproach.OnceaJSFbackingbeanbecomesamanagedbean,itwillbeautomaticallyinstantiatedandinitializedwheneverit’saccessedforthefirsttimeinthecontextassociatedwiththebean’sscope.Itwillbeautomaticallydestroyedwhenthelifecycleassociatedwiththebean’sscopehasended.Moreaboutmanagedbeanscopesinthenextsection.

Historically,JSFprovidedanativewaytoregisterbackingbeanclassesasmanagedbeans:first,inJSF1.xvia<managed-bean>entriesinfaces-config.xml,andsinceJSF2.0via@javax.faces.bean.ManagedBeanannotation,whichis,sinceJSF2.3,officiallydeprecatedinfavorofCDI@Named.CDIwasintroducedforfirsttimeinJavaEE6,atthesametimeasJSF2.0,withtheaimofunifyingthemanagementofcontext-sensitiveinstancesandinjectingthecurrentlyavailableinstancesineachother.Unfortunately,theJSF2.0@ManagedBeanwasalreadysetinstonelongbeforeCDIwasfinished,sothosetwowaysofmanagingbeansdidexistinparallelforsometime.TheCDIbeanmanagementfacilityhasseveraladvantagesontopofJSFbeanmanagementfacility.

First,injectingonemanagedbeaninanothermanagedbeanusingCDI’s@Injectdoesn’trequireagetter/setterpairintheparentbackingbeanclass,whileJSF’s@javax.faces.bean.ManagedPropertyrequiresagetter/setterpair,whichisconsideredpoorpracticeasthisexposestoomuchinformationtotheoutside,whichispotentiallyconfusing.Shouldweaccesstheinjectedbeanvia#{bean}or#{parentBean.bean}?

Second,theinjectedCDImanagedbeancanbeofa

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

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

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

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";

}

}

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

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

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

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

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

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.

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(){

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”

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.

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.

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

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">

<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

...

<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

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

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

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

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">

<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}">

<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.

<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;

}

}

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;

@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

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

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

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

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

instances.ThelegacyJSF@ManagedBeanfacility,however,didnotuseproxiesatall.That’sexactlywhyitwasimpossibletoinjectaJSFmanagedbeanofanarrowerscopeinaJSFmanagedbeanofabroaderscope.WithCDIbeanmanagementfacilitythisisjustpossible.

NotethatCDIhasalsoa@javax.enterprise.inject.Modelstereotypeannotationwhichbasicallybundlesboth@Namedand@RequestScopedintoasingleannotation.Thisisinnowaydifferentfromarequest-scopedmanagedbean.Unfortunately,itdoesnotrepresentanon-proxyinstance;otherwiseitwouldbenicetoputitonan@Entity.The@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},

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);

}

publicProductgetProduct(){

returnproduct;

}

}

Notethatyoustillcan’tuse<h:inputTextvalue="#{product.name}">,becauseitwouldgetitsowninstance.Youstillneedtouse#{products.product.name}.Forexactlythisreason,theproducerisn’t@Named.Alsonotethatincaseofaview-scopedmanagedbean,you’dneedtoforceJSFtorestarttheviewscopebyreturninganon-nulloutcomefromactionmethod;otherwisetheinjectedProductinstancewouldbereusedforthenextview.

Whichscopetochoose?Whichscopetochoosedependssolelyonthedata(instancevariablesakathestate)thebeanholdsandrepresents.Youshouldstrivetoputthestateintheshortestpossibleacceptablescope.Startwitha@RequestScopedbean.Onceyounoticethatsomestateneedstoberetainedafterapostbackonthesameview,splitthatstateexactlyintoanew@ViewScopedbeanwhichyou,inturn,@Injectinthe@RequestScopedbean.OnceyounoticethatsomestateneedstoberetainedonanotherGETrequestwithinthesamesession,splitthatstateexactlyintoanew@ConversationScopedbeanwhichyouinturn@Injectinthe@RequestScopedbean.Andsoon.

Abusingan@ApplicationScopedbeanforsession-,conversation-,flow-,view-,orrequest-scopeddatawouldmakeittobesharedamongallusers,soanyoneelsecansee

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

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;

}

}

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

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}">

</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.

@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

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

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(  )

@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(  )

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

@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

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)

{

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());

}

}

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

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.

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();

@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

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

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.

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.

(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

Figure9-1 ThedefaultHTTP500errorpageofWildFly11.0.0

TheJSF(JavaServerFaces)implementationbeingusedmayevenprovideitsowndefaulterrorpage.BothMojarraandMyFacesprovideaninternaldefaultimplementationbasedonthejavax.faces.context.ExceptionHandlerAPI, whichisonlyshownwhentheJSFprojectstageissetto1

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

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

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>

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).

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>

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);

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(){

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>

<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

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

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

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

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);

}

}

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

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.

<!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

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

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

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);

}

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

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());

}

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.

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();

}

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();

}

}

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

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

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.

(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

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){

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

<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

@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

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;

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.

<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

=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

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

<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;

@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)

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;

@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);

}

}

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;

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

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

application-scopedbeanobserve@WebsocketEvent.Openedand@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(

);

}

}

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

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

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.

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

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.

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.

(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

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);

}

@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);

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.

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=

"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>

</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

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.

<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

<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

<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

<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

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

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

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...>

...

</t:dataList>

Now,youcanessentiallyremoveallattributesofthe@FacesComponentsothatitbecomesjust@FacesComponent.Yes,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

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

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)

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

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

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){

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()),

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

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

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);

}

}

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.

@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());

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

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

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();

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,

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

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

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

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

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>

</renderkit>

No,youcannotusethe@FacesRendererannotationforthis.Thiswon’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

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();

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();

}

@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

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

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,

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.

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

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

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,youmayfindthe@javax.faces.application.ResourceDependenc

yannotation useful.Imaginethatyouwouldliketoautomaticallyincludecommon:scripts/some.jsandcommon:styles/some.cssalongwithaparticularcustomcomponent;thenyoucandosoasfollows:

12

@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-.

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.

(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

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

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

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

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

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"/>

</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

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){

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

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

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

@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.

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)}">

<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

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

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

@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

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

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

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

.

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-.

(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

(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

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

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

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

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

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

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>

<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

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”.

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

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

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

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="admin@example.com",

password="secret1",

groups={"VIEW_USER_PAGES","VIEW_ADMIN_PAGES"}

),

@Credentials(

callerName="user@example.com",

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—

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{

//...

}

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

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;

}

}

}

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

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="#

{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{

@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

}

}

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;

@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(),

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",

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

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

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.

@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(),

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

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

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

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.

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

){

@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.

@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

toinsertanextramanuallycreated“pass-throughwrapper”HttpAuthenticationMechanismWrapperinstanceasshowninthecodeabove.Thecodeofthiswrapperisasfollows:publicclassHttpAuthenticationMechanismWrapperimplementsHttpAuthenticationMechanism

{

privateHttpAuthenticationMechanismwrapped;

publicHttpAuthenticationMechanismWrapper(){

//

}

publicHttpAuthenticationMechanismWrapper

(HttpAuthenticationMechanism

httpAuthenticationMechanism)

{

this.wrapped=httpAuthenticationMechanism;

}

publicHttpAuthenticationMechanismgetWrapped(){

returnwrapped;

}

@Override

publicAuthenticationStatusvalidateRequest(

HttpServletRequestrequest,

HttpServletResponseresponse,

HttpMessageContextcontext)throws

AuthenticationException

{

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

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

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

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;

@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();

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

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

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;

}

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;

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>

<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')}"/>

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

“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.

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

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>

</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"

>

<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

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>

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.

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

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>

</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

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(...){

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.

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>

</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.

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>

<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

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>

(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

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

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

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>

<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

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.

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="#

{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);

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

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.

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

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,using@allisgenerallyconsideredabadpractice.There’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

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

'.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>

<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_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"

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

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

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']}"/>

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)

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.

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"

/>

</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

|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)

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

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>

{

@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();

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

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

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

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.

(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

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

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;

}

@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

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

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

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

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";

}

};

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"

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

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

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;

}

@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;

}

}

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.

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:

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

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

<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>

<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

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:

@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);

}

@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"

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

(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

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

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.

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

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

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

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

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

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

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)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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>

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

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

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

top related