10

Unfortunately, I made a mistake of choosing JSF for an internet facing, high traffic application, now I am wondering as to how to improve the scalability of this JSF webapp.

I have a JSF page that displays a large no of items each of which may be commented upon. Inorder to reduce the state & improve performance I am trying to reduce the no of forms /commandButtons on the page.

1. Through what ways can I reduce the component tree/ statefulness of JSF ? Do the plain html elements(that are mixed in between the jsf tags) also form part of component tree ? I dont know how component state saving has been helpful to my app since I have been following plain request/response model while designing my app, (may be it is helpful for just JSF's internal requirements)!?

2. I was thinking of an approach where instead of creating a separate <h:form> (each with a separate commandButton) for every item like below,

(Usual Approach)

<h:form> <!-- for each item a separately -->
      <h:outputText value="Add comment"/>
      <h:inputTextarea value="#{itemController.comment}"  required="true"/>

      <p:commandButton actionListener="#{itemController.addUserComment(123)}" value="Add" />
</h:form>

(Alternate Approach)

I am trying to make the above better by just putting a single remoteCommand for all the items & pass the required parameters to this remoteCommand.

<form>
   <input id="item1_comment"/>
   <button onclick="addComment(123, 'item1_comment');"/>  
</form>    

<script type="text/javascript">
    function addComment(itemId, id) {
        $('#comment_in').attr('value', $('#'+id).attr('value'));
        $('#forItem_in').attr('value', itemId);
        addComment_RC(); // call remoteCommand to show the content in dialog
    }
</script>

<h:form prependId="false" >  <!-- for all items, just single remoteCOmmand -->
    <h:inputHidden id="comment_in" value="#{itemController.comment}"/>
    <h:inputHidden id="forItem_in" value="#{itemController.forItem}"/>
    <p:remoteCommand name="addComment_RC" process="@form" actionListener="#{itemController.addComment()}" />
</h:form>

Is it better to do it this way (or are there any issues with this approach)?

Community
  • 1
  • 1
Rajat Gupta
  • 25,853
  • 63
  • 179
  • 294
  • 1
    If you are using ajax, why not using one form for all elements? – Matt Handy Mar 15 '12 at 12:03
  • 1
    does that reduce the state? & does it make more sense to keep all data that shouldn't be processed all at once under a single form ? – Rajat Gupta Mar 15 '12 at 14:35
  • Yes it will reduce complexity and you can instruct jsf to only process the form elements you are interested in with the `execute` attribute of `f:ajax` – Matt Handy Mar 15 '12 at 14:40
  • yes the complexity of this alternate approach would be avoided but since the no of commandButtons remain the same, how is the state reduced? – Rajat Gupta Mar 15 '12 at 14:49
  • You could do it without buttons. I think of the blur or keyup events for the textfields. – Matt Handy Mar 15 '12 at 14:52
  • I like you way to do it. Have you tried it? – Mikita Belahlazau Mar 15 '12 at 17:36
  • @Nikita: Yes I have been using this approach until now & it works all fine, but I just wanted to know if anyone can point to me about the bad part(if any) of that. – Rajat Gupta Mar 15 '12 at 17:39
  • @user do you update page somehow on button click? Or just send ajax request? – Mikita Belahlazau Mar 15 '12 at 17:44
  • I had been just sending an ajax request until now but updating entire page would be very easy, just put in remoteCommand's update. For updating just a single specific component, I guess remoteCommand's update wont be useful in this case, as it needs to be dynamically set. Though some other alternative arrangements may be made for that – Rajat Gupta Mar 15 '12 at 18:02
  • @user you can try using primefaces javascript ajax api for sending request. In this case you're more flexible, you can choose id for updates in every request and handle response from server. – Mikita Belahlazau Mar 15 '12 at 18:07
  • @NikitaBeloglazov: Could you please tell how to substitute remoteCommand's `actionlistener` with ajax api? I couldn't find this in the docs.. – Rajat Gupta Mar 17 '12 at 14:35
  • 4
    I strongly recommend you take a heap dump of your application, and examine it to identify what exactly is using the memory. You may be optimizing the wrong part of your program if you skip that. – meriton Mar 18 '12 at 17:10
  • I think this is not the most suitable introduction for a question that shall be answered by experienced JSF users. Maybe the bounty will compensate that. – Matt Handy Mar 18 '12 at 19:36

1 Answers1

6

Performance issues in the situation you describe are often caused by the large number of EL expressions, That burdens the server.

One approach to tackle this issue is to compute the comments on the client side, and pass them all at once to the server. Thus reducing the number of comment EL expression to one or none, and use only one button.

Place all the elements in one form. The comments fields are not binded.

  <h:form>

       // first element 
       <h:outputText value=#{first element}
       // first comment 
       <h:inputTextarea id="comment1"/> <-- notice there is no EL expression 
                                            But we use a unique id for each comment 

       // second element 
       <h:outputText value=#{second element}
       // second comment 
       <h:inputTextarea id="comment2"/> 
       .
       .
       .



</h:form>

From here you could either

1. after each blur event in any of the comment fields, ajax the server and pass as parameters the comment and the id of the comment from which the ajax call was made. on the server update your model accordingly

Or You can also gather all the comments on the client side and send them to the server at one time.

2. When the user press the submit button call a js function to aggregate all the comments in a structure that you will be able to parse easily on the server side (ie. "{c1,comment a};{c2,comment b};{c5=,comment e}..."). pass that string to the server, parse it and update your model accordingly.

3. after each blur event in any of the comment fields, call a js function that updates an hidden field.

<h:inputHidden value="{myClass.allComments}" />

when the user submits the form parse allComments and update your model accordingly.

EDIT:

To address the general performance issue I've added recommendations from an article that I found helpful speed up part 1 Speed up part 2.

Hope this helps

btw, I would recommend the first approach rather than the last two.

Aba Dov
  • 962
  • 4
  • 12
  • 20
  • I guess what you're suggesting in point1 is almost similar to my alternate approach(described above) , only difference is that I am using a non-jsf html `button`, instead of blur event, to trigger submit to hiddeninput & then trigger update on server. – Rajat Gupta Mar 16 '12 at 04:18
  • 1
    @user Yes, I followed the same approach, and implemented the following principals 1.use one form (no need of more in this case it will only complicate matters) 2. Important- using instead of might cause problems rendering the elements 3.reduce the amount of el expressions 4. generate the response comments on the client side. 5. use ajax to call the server 6. update the model indirectly on the server. – Aba Dov Mar 16 '12 at 05:52
  • thanks, could you also clarify how might using instead of cause problems rendering the elements ? I have been using it with no problems, right now you can safely mix jsf components with plain html tags.. – Rajat Gupta Mar 16 '12 at 07:06
  • @user You are correct. There are certain situations that may cause issues.for example creating div with jsf http://www.coderanch.com/t/545977/JSF/java/JSF-div-tags (http://stackoverflow.com/questions/951593/what-jsf-component-can-render-a-div-tag) and using library divs as tomahawk. one approach is to always let the jsf decide on how to render the element. you have a large number of items to render, so I would start by using tagged div and input elements, and only after checking functionality consider removing them – Aba Dov Mar 16 '12 at 07:24