10
votes

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

1
If you are using ajax, why not using one form for all elements?Matt Handy
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
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:ajaxMatt Handy
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
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

1 Answers

6
votes

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.