5

I have an Action in request scope. The return value of one of its methods is passed around to a custom facelet tag. This tag then extracts several attributes of the returned object and displays them. The problem is the EL expression which has the method call on the Action is called for every evaluation of the attribute of the returned object. I will put the relevent pieces of code here.

some.xhtml

<ui:include src="someOther.xhtml">
    <ui:param name="profileUri" value="#{param['relateToProfile']}"/>
    <ui:param name="qualifier" value="#{param['qualifier']}"/>
    <ui:param name="cellStyleClass" value="#{param['cellStyle']}"/>
</ui:include>

someOther.xhtml (approach 1) Note that ProfileAction is in @RequestScoped

<tenui:entityCard profileEntity="#{profileAction.getProfileMetadata(profileUri)}"
  qualifier="#{qualifier}"   
  cellStyleClass="#{cellStyleClass}"/>

enityCard.xhtml(facelet custom tag)

<ui:fragment rendered="#{profileEntity.featured}">...
<tenui:gridCell id="#{profileEntity.profileId}#{qualifier}" ...      
 <tenui:metaunit ..content="#{profileEntity.getMeta('memberName')}" 
  href="/#{profileEntity.profileDisplayUri}" 
  hrefStyleClass="a-styled grid-cell-name"/>
  .....
  ...several other EL expressions including #{profileEntity.xxx} 

The problem is #{profileAction.getProfileMetadata(profileUri)} is being called for every attribute evaluation in entityCard.xhtml Then, I thought I would save the return value of method call in a c:set var(approach 2 as noted below) but it doesn't help.

someOther.xhtml (approach 2)

<c:set var="profileMetadata" 
       value="#{profileAction.getProfileMetadata(profileUri)}"/>
<tenui:entityCard profileEntity="#{profielMetadata}"
  qualifier="#{qualifier}"   
  cellStyleClass="#{cellStyleClass}"/>

The action method calls a Stored proc which is quite expensive and the returned object has over 20 attributes that get evaluated in ELs in entityCard.xhtml.

I also tried another approach with resolving teh value at ui:param itself by calling the action method directly, but of no avail at all. The problem remained.

Can someone point to what could I be doing wrong? Or, how I could avoid the multiple calls to profileAction.getProfileMetadata call?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Ashesh Nishant
  • 103
  • 1
  • 10
  • Have you tried to change action scope to `ViewScoped`? It should help. – pepuch Apr 04 '13 at 05:11
  • I could try but I don't quite get the rationale behind it. There is an Ajax call that fetches the entitycard and I really don't want the Bean to persist beyond that. Additionally, why would changing the scope to View make any difference to the EL evaluations? – Ashesh Nishant Apr 04 '13 at 05:43
  • It was just suggestion, `ViewScoped` lives as long as user is in the same view. You wrote that there are ajax calls. If so, scope will be recreated every ajax call. Maybe thats why your method is called every time. – pepuch Apr 04 '13 at 06:00
  • Sorry, I should have explained better. There is one single one single ajax POST call(duly verified by Firebug Console), which loads some.xhtml, passing it 3 parameters. That, in turn, calls someOther.xhtml, which initializes a custom tag with the return value of a `Requestscoped` bean method. The custom tag then evaluates several attributes of that returned Object. But instead, it seems to be evaluating the method call itself for every attribute that it needs to fetch on the returned object! – Ashesh Nishant Apr 04 '13 at 06:06
  • instead of ``, can you try ``? – Mr.J4mes Apr 04 '13 at 07:21
  • I tried that as you can see in the code above. Only when it didn't work, did I try approach 2 with c:set. – Ashesh Nishant Apr 04 '13 at 09:13

1 Answers1

4

You need to set the scope attribute of <c:set> to one of the desired scopes, request, view, session or application. Otherwise it defaults to none.

Assuming that you want it to be request, this should do:

<c:set var="profileMetadata" scope="request"
       value="#{profileAction.getProfileMetadata(profileUri)}" />
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • WOW! That works like a charm! Thanks a ton! I am still curious about if I didn't use c:set at all why would that method call in EL be called repeatedly. – Ashesh Nishant Apr 04 '13 at 13:03
  • Because the evaluation is by default deferred. The outcome is by default not stored in any scope. Please note that invoking business logic in getter methods is considered bad practice. See also http://stackoverflow.com/questions/2090033/why-jsf-calls-getters-multiple-times/2090062#2090062 Your case is however probably a corner case, can't tell that reliably based on the information provided so far. – BalusC Apr 04 '13 at 13:14
  • Thanks! Would ${...} instead of #{...} have forced an immediate evaluation? – Ashesh Nishant Apr 05 '13 at 07:03
  • In JSP, yes; in Facelets, no. See also http://stackoverflow.com/questions/4812755/difference-between-jsp-el-jsf-el-and-unified-el/4812883#4812883 Just stick to `#{}` in Facelets all the time. – BalusC Apr 05 '13 at 10:53