domingo, 26 de junio de 2011

JSF component state per row for datatables

Components like h:dataTable, ui:repeat and others found in common JSF libraries (in the current moment JUN 2011) does not preserve component state per row. This is an old known problem found in JSF, but fortunately there is an alternative to solve this one.

But first it is necessary to explain the problem with a simple example. The code shown below does not work as users expect:

<h:form id="mainForm">
  <h:dataTable value="#{itemsBean.items}" var="item">
    <h:column>
      <h:outputText value="#{item}"/>
      <h:commandButton value="Change Only this Button To Red" actionListener="#{itemsBean.changeToRed}"/>
    </h:column>
  </h:dataTable>
</h:form>

@ManagedBean(name="itemsBean")
@SessionScoped
public class ItemsBean{

 
    private List<String> items;
   
    public List<String> getItems() {
        if (items == null) {
            items = new ArrayList<String>();
            items.add("A");
            items.add("B");
            items.add("C");
            items.add("D");
        }
        return items;
    }
   
    public void changeToRed(ActionEvent evt) {
        evt.getComponent().

            getAttributes().put(
                "style", "background:red");
    }
}

The previous code renders 4 rows with the item text and a button:

A [Change Only this Button To Red]
B [Change Only this Button To Red]
C [Change Only this Button To Red]
D [Change Only this Button To Red]

What users expect is when a button is clicked, only the button who was clicked changes to red. Variants of this code includes calls to UIComponent.invokeOnComponent, UIComponent.visitTree or UIComponent.findComponent that tries to change something on the component state inside the row and keep it on that scope.

But the real behavior is all buttons change to red. In other scenarios state could get lost.

The reason behind this behavior is tags like h:dataTable or ui:repeat only save properties related with EditableValueHolder interface (value, submittedValue, localValueSet, valid). So, a common hack found to make it work correctly is extend your component from UIInput or use EditableValueHolder interface, and store the state you want to preserve per row inside "value" field. One example of a component using that approach is tomahawk t:collapsiblePanel. But note this only works with simple components, and use that strategy is just workaround.

The most simple solution since JSF 1.2 using facelets is refactor the code to use c:forEach tag:

<h:form id="mainForm">
 <table>
  <c:forEach items="#{itemsBean.items}" var="item">
    <tr>
      <td>
        <h:outputText value="#{item}"/>
        <h:commandButton value="Change Only this Button To Red" actionListener="#{itemsBean.changeToRed}"/>
      </td>
    </tr>
  </c:forEach>
 </table>
</h:form>

It works, because c:forEach is a "build view" tag, or in other words, just traverse the list first time the view is rendered, and create full components per row. So in the component tree we have 4 component buttons instead one "shared" by all rows.

The problem with use c:forEach is that create components makes state bigger and with many rows, view rendering is slower. That's the reason why h:dataTable or ui:repeat create just one component and share it for all rows: to keep state small and view rendering fast.

There are other workarounds to this one, like use EL expressions and store some properties on the model. All those solutions are valid, but isn't exists a clean approach for this one? something that preserve the component state on the component and the model data on the model, without use a proxy pattern?.
  
The good news is exists one cool hack to make it work since JSF 2.1, and a extended version of this hack is available on tomahawk latest snapshot, so it will be in version 1.1.11. This will only work with partial state saving enabled.   

Since JSF 2.1, UIData implementation has a new property called rowStatePreserved. Right now this property does not appear on facelets taglib documentation for h:dataTable, but on the javadoc for UIData there is. So the fix is very simple, just add rowStatePreserved="true" in your h:dataTable tag:

<h:form id="mainForm">
  <h:dataTable value="#{itemsBean.items}" var="item" rowStatePreserved="true">
    <h:column>
      <h:outputText value="#{item}"/>
      <h:commandButton value="Change Only this Button To Red" actionListener="#{itemsBean.changeToRed}"/>
    </h:column>
  </h:dataTable> 
</h:form>

That's it. Simple, isn't it?.

Tomahawk t:dataTable and t:dataList have rowStatePreserved property too, so if you are in JSF 2.0, you can use those tags instead:

<h:form id="mainForm">
  <t:dataTable value="#{itemsBean.items}" var="item" rowStatePreserved="true">
    <h:column>
      <h:outputText value="#{item}"/>
      <h:commandButton value="Change Only this Button To Red" actionListener="#{itemsBean.changeToRed}"/>
    </h:column>
  </t:dataTable>
</h:form>

<h:form id="mainForm">
 <table>
  <t:dataList value="#{itemsBean.items}" var="item" rowStatePreserved="true">
    <tr>
      <td>
        <h:outputText value="#{item}"/>
        <h:commandButton value="Change Only this Button To Red" actionListener="#{itemsBean.changeToRed}"/>
      </td>
    </tr>
  </t:dataList>
  </table>
</h:form>

Additionally t:dataTable and t:dataList are designed to handle complex scenarios like nested combinations. If you need to add/remove rows and keep state per row working in a reliable way you can use "rowKey" property to identify in a unique way a row, so that value will be used later to generate child component clientId and the final effect is your component state will be preserved even when you manipulate the model. But that's for another blog entry ;-).

Please note in current Mojarra implementations (2.0.6 and 2.1.2) there is a problem with composite components that makes state get lost (see JAVASERVERFACES-1985 for details), but this work on MyFaces 2.0.7 and 2.1.1, so give it a try ;-). Anyway, Mojarra guys are working hard to get it out, it is a very difficult problem and I have been helping them with it, but I expect sooner or later this problem will be solved.

sábado, 25 de junio de 2011

10 reason why choose MyFaces Core as JSF implementation for web applications

A common question on the internet about JSF is "... which JSF implementation choose for my web application? ...". Given that there are two implementations of JSF spec (MyFaces and Mojarra), and from a common developer point of view both do the same (both implement a standard specification), it is difficult to see which choice works better for your particular needs. Note this is written on June 2011, and the version we are taking about is the latest ones available 2.0.7 and 2.1.1.

Well, in this blog I'll give you 10 reasons why MyFaces Core should be your choice. After read this, is your choice to decide which one take for your next web project.
  1. Community over code: MyFaces community counts with a lot of folks with outstanding knowledge on JSF. Suscribe to user and dev mailing lists are the best way to know what's going on, receive feedback and know other people interested in JSF. This is an important consideration, because sometimes you can find a bug or need to ask something to somebody about JSF.
  2. Innovation happens on MyFaces: MyFaces is not just a JSF implementation, it host other projects (Trinidad,Tobago, Tomahawk, ExtVal, CODI, Orchestra, PortletBridge RI, ....) that provides additional functionality not provided by JSF implementation itself. Many of these projects are compatible with both MyFaces and Mojarra, but you should note that those projects helps MyFaces Core to keep code in good shape, because all those projects test against MyFaces Core, and if there is a bug, it is handled more quickly (because everything is handled "in home").
  3. MyFaces Core has better compatibility with facelets 1.1.x: Sometimes it is easy to find some use cases with combinations of c:if, ui:include, c:forEach and other tags that causes problems when they are upgraded to JSF 2, because it has some problems with partial state saving. Just set org.apache.myfaces.REFRESH_TRANSIENT_BUILD_ON_PSS web config param to true and org.apache.myfaces.REFRESH_TRANSIENT_BUILD_ON_PSS_PRESERVE_STATE to true, and a special mode will be activated.No c:if tags or c:forEach or ui:include broken anymore, because some parts of the tree are marked to be restored fully, just like on JSF 1.2.
  4. MyFaces Core composite component feature is built on top of facelets template client mechanism: special care has been taken to ensure tags like ui:composition, ui:decorate are correctly resolved, even inside composite components and other complex cases. No "component relocation" is used for implement cc:insertChildren and cc:insertFacet like in Mojarra, so that means state will not get lost inside composite components. This issue in Mojarra is really serious, but people are working there to get it fixed. 
  5. MyFaces state manager takes into account multiple window cases: Since 2.0.7 and 2.1.1, a new web config param
    org.apache.myfaces.NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION was added. This param is only applicable if state saving method is "server" (= default). Indicates the amount of views (default is not active) that should be stored in session between sequential POST or POST-REDIRECT-GET if org.apache.myfaces.USE_FLASH_SCOPE_PURGE_VIEWS_IN_SESSION is true. For example, if this param has value = 2 and in your custom webapp there is a form that is clicked 3 times, only 2 views will be stored and the third one (the one stored the first time) will be removed from session, even if the view can store more sessions org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION. This feature becomes useful for multi-window applications. where without this feature a window can swallow all view slots so the other ones will throw ViewExpiredException.
  6. MyFaces server side state saving does not use a buffer to place javax.faces.ViewState token: Before 2.0.7 /2.1.1 and on Mojarra, a buffer is used to store temporally everything before the end of the first found form. Then and algorithm replace javax.faces.ViewState token with the state and the output is flushed. MyFaces uses a clever strategy to that prevents use that buffer when server side state saving is used, reducing memory consumption over requests.
  7. MyFaces Core cache EL expressions when necessary: this will be a new feature proposed for 2.0.8 and 2.1.2 (not available yet but you can try on a snapshot), and in few words that means no EL expressions will be created if is not necessary. This have an important impact on memory comsumption and speed.
  8. MyFaces Core is "OSGi" friendly: It provides some SPI interfaces to deal with special setups, when you need core control over classloading. A lot of good stuff has been put in this area.
  9. MyFaces Core provide uncompressed versions of javascript files: when project stage is development, you can set org.apache.myfaces.USE_MULTIPLE_JS_FILES_FOR_JSF_UNCOMPRESSED_JS web config param to true and the base javascript files will be used instead the compiled bundled jsf.js. If it is false, by default the bundled jsf.js but uncompressed will be available. That makes easier find ajax bugs, which usually tends to be difficult to solve.
  10. MyFaces Core generates code using myfaces-builder-plugin: these project is a maven plugin that with myfaces-builder-annotations provides a jsf development kit to create multiple artifacts (components, converters, validators, client behaviors) and handle all documentation related in an easier way, minimizing the number of files you need to handle. It uses java annotations to feed all related information, and each myfaces jar using this tool has a myfaces-metadata.xml which contains all meta-information required to build each bundle. This tool can read composite component definitions and build an integrated documentation with your normal components, converters or validators.
These 10 reasons are only the "tip of the iceberg". There are more reasons why choose MyFaces, but I hope the previous ones are good enough to convince you to at least give it a try!.