Sunday 25 November 2012

Advanced PrimeFaces Graphic Image renderer for dynamic content

PrimeFaces has a component to render graphic resources and has added support for dynamic generated content.
There is a special StreamedContent class for this, so that you stream the generated resources from the JSF application to the browser.

But there are a few limitations for the component when it comes to dynamic generated content.  And they are not related to PrimeFaces but to the communication model between the browser and the application server.
Since there were lately a few questions in the PrimeFaces forum that had to do with these limitations, I decided to create an advanced renderer for the component. But there are a few warnings in using it.
For the impatient ones, you can find the project on GitHub.

How does the PrimeFaces Graphic Image renderer work?

The PrimeFaces Graphic Image renderer generates a html img tag where the src attribute points back to the application.  The URL contains a unique id and stores within the http session of the user, the EL expression linked to that unique id.
The EL expression comes from the value attribute of the p:graphicImage tag that the developer wrote in the JSF file and which points to the StreamedContent value that contains the dynamic generated content.
The browser interprets the html page and ask for the image content based on the URL found in the src attribute.

Since this URL points back to the JSF application, the PrimeFaces resource handler gets a chance to handle the request.  It recognize that the browser asks for a graphic resource with dynamic content based on the URL parts and parameters.
Based on the unique id which is available as parameter in the URL, it can take the corresponding EL and stream the content to the browser.

At this last step, the limitations can be hit in some use cases. Since the resource handler has no idea whatsoever of the page where the dynamic content is located, which bean is involved etc...  It can only rely on the EL expression the renderer has placed in the http session.  It is possible that this EL expression is only valid within a certain context.

The most striking example is the use of a composite component attribute.  Something like #{cc.attr.image} can be perfectly valid within the context of a custom composite component but it is clearly not when arbitrary executed.
The same reasoning goes for the dataTable component.  Whenever you use the alias defined in the var attribute for indicating the dynamic content, the PrimeFaces resource handler will be unable to retrieve the contents.
And the renderer can't determine the  EL expression that can give us the reference to the StreamedContent

Solution

The solution that I implemented is that we store the contents of the dynamic generated content on a location that can is always accessible, without the restriction of a context. This means we need to copy the contents to another location, the file system in our case.
The renderer copies the StreamedContent to a file in the System temp directory. For scaling reasons, this is a better alternative then storing the content directly in the http session area. We now only need to keep the file name next to the unique id instead of the EL expression.

On the other side, there is also another resource handler defined that uses the contents of the file to serve the browser request for the image resource.
The temporary files are removed when the http session is invalidated so that we don't have a problem with the disk storage size. We can't do it earlier because the browser can request the image multiple times as the user goes back in the screen flow or asks for a refresh.

Usage warnings

There is one issue with the above described solution, there is a performance drop.  It all depends on the size of the images of course but we need to write the content to disk, an additional step, and the resource handler serve it from disk which is slower then memory access.
For optimal performance, the storage on the disk is performed by using java.nio.channel.Channels.  That should guaranty to most efficient solution.

The clearing of the files is done when http session is invalidated by a HttpSessionBindingListener.  And although we can be pretty sure that the files gets removed, if the server implementation is correct, the files are kept during the user session.  When you have an application with a lot of dynamic generated content and the user works for a long time with your application, a lot of files can be created.

Intelligence of the advanced renderer

Therefore, there is some intelligence build into the Graphic Image renderer.  The scenario where the content is stored on disk is by default only used when the graphicImage tag is used within a custom composite component or a component that implements the UIData interface like dataTable.
These 2 cases are the situations where there is a lot of chance that the standard algorithm of PrimeFaces will fail. If this is not the case, the standard PrimeFaces functionality will be used (without the storage on disk).

Intelligent default functionality is fine but sometimes you need to take control yourself. This can be achieved by nesting a advancedRenderering tag within the graphicImage tag.  By specifying a boolean value for the value attribute, you can control the usage of the advanced rendering functionality.

Use cases where this can be handy.
1) You are using the graphicImage tag within a custom composite component but you as developer know that the EL you have specified can be used outside of the component (it is 'general' and not tied to a certain context) then you can disable the advanced rendering.
2) You have created the dynamic content with a bean on a request scope.  The resource handler can perfectly access the bean but in most cases the generated content is lost.  Here you can ask for the advanced rendering so that at render time, the image is stored on disk ready for streaming to the browser.

Code

The code can be found on GitHub and is released under the Apache License, Version 2.0.

Friday 2 November 2012

Disabled selection button for PrimeFaces data table

Introduction

When you use the disabled attribute of a button, you can have some troubles executing the action and actionListener bound targets. The same goes for an input component where you can loose the user input.
But there are various use cases where an initial disabled button could be vary handy. The easiest solution could be that you change the scope of the managed bean the property is located that determines the value of the disabled attribute. But in this post I’ll describe an alternative and why it is maybe the best solution.

Disabled button problem

In the Apply request values phase of the JSF life cycle, you can find various resources on the internet that explain this important concept of JSF, there is a check if the post values contains the id of the button.
In that case, the specific button is clicked and an event is generated that the logic attached to the button needs to be executed in the Invoke application phase.
This kind of check is required because a form can contain multiple buttons and thus we need to know which functionality needs to be executed.  But the check that the button must be enabled can give us sometimes a problem.
The EL expression specified in the disabled attribute is evaluated in the Apply request values phase and when it evaluates to true, the event isn't posted.
So even when the button is enabled on the screen, during the Apply request values phase
of the JSF lifecycle, the EL expression can return a disabled true value and thus the click on the button has no effect.

Selection button for PrimeFaces data table

So, let us try with the PrimeFaces data table. You have various row selection options but I want to use the one where a button is placed in a footer of the table.
Suppose you have a JSF managed bean called personListView on a view scope that contains the list of records that you need to show in the page.
The view scope will be used in a lot of cases for the backing of a data table as you don't need to read the data in the database every time you change the sorting or filtering. This of course if the list of records you want to show isn't too long and read them all at once in memory.
The second managed bean, personView called, is on request scope and will be used to edit the selected record. It is on request scope because it only need to capture the data entered by the user and save it to the database.
The JSF page contains a section like this:
<p:dataTable value="#{personListView.personList}" var="person" selectionMode="single"
                     selection="#{personView.selectedPerson}" rowKey="#{person.id}">
   <p:column headerText="first name">
      ...
   </p:column>
   <f:facet name="footer">
     <p:commandButton ajax="false" value="Edit" action="person.jsf"
                      actionListener="#{personView.checkPerson}"/>
   </f:facet>
</p:dataTable>

In the footer, the button is shown to navigate to the second page, person.jsf, and calls here for demonstration purposes an actionListener method that just add an information message.

The idea is that the edit button stays disabled as long as you haven't selected a record in the data table.

So we change the markup to use the disabled attribute of the commandButton and an ajax call that listens to a row selection so that we can ask for a re-rendering of the button.
<p:dataTable value="#{personListView.personList}" var="person" selectionMode="single"
                     selection="#{personView.selectedPerson}" rowKey="#{person.id}">
  <p:ajax event="rowSelect" update="editBtn" />
  <p:column headerText="first name">
                ...
  </p:column>
  <f:facet name="footer">
    <p:commandButton ajax="false" value="Edit" action="person.jsf"
                     actionListener="#{personView.checkPerson}" id="editBtn"
                     disabled="#{empty personView.selectedPerson}"/>
  </f:facet>
</p:dataTable>    

Now the button is disabled as long as we don’t click on a row in the data table.  If we do so, the ajax event is fired, it sets the selected record to the selectedPerson property of the personView managed bean. The re-rendering of the button enables it so that we can click on it.

But if we do click on it, nothing happens. The first thing you should check with a PrimeFaces commandButton that is supposed to do navigation, is that we aren't using ajax.  But in this case, we haven't made any mistake. (ajax=”false” attribute on the commandButton)

The reason why the click on the button has no effect is because of the disabled attribute.  As described above, during Apply request values phase, the EL expression points to a new instance of the personView managed bean, it is at request scope, and thus the selectedPerson property is still null. It will be filled during the Apply model values phase, later on in the life cycle.

Scope change as a solution?


The easiest solution is to change the scope of the personView managed bean to view scope. The bean is first initialized when the ajax call is executing for the row selection. And the bean is kept alive for the click on the button and thus the selectedPerson value has still a value and the button is processed as we expect.

But view scope is a kind of session scope. Surely it has his advantages and usages but a bean with a view scope is stored in the session of the user.  Additional processing is required to remove the bean from the session storage when the user navigates to another view.  So in terms of processing time and server memory consumption, a view scoped bean is worse then a session bean.

So my first reflex is always to use beans on a request scope.  When the situations justifies to use another, longer scope, I will use it of course. So can we fix the above described problem by keeping the personView on request scope?  Yes we can.

The binding attribute.


The solution lies in the binding attribute that each JSF component has. Using this attribute can lead also to some problems (maybe this can be the inspiration for another blog item) but here it makes the selection button responsive again.

The binding attribute gives you to opportunity to have a reference in your backing bean to the java object instance that is used on the component tree representation of your view. It allows programmatic access and changes to the component. So JSF view fragment becomes this:
<p:dataTable value="#{personListView.personList}" var="person" selectionMode="single"
                     selection="#{personView.selectedPerson}" rowKey="#{person.id}">
   <p:ajax event="rowSelect" update="editBtn" listener="#{personView.enableEditBtn}"/>
   <p:column headerText="first name">
      ...
   </p:column>
   <f:facet name="footer">
      <p:commandButton ajax="false" value="Edit" action="person.jsf" binding="#{personView.button}"
                       actionListener="#{personView.checkPerson}" id="editBtn"
                       disabled="#{empty personView.selectedPerson}"/>
   </f:facet>
</p:dataTable>

It has 2 additions; the binding attribute on the commandButton and the listener attribute on the ajax rowSelect tag.

The binding refers to a property in the request scopes personView bean of type HtmlCommanfButton. During the first phase of the life cycle, Restore view Phase, it gets the reference to the java object instance on the component tree for the selection button.
 
private HtmlCommandButton button;

// Setter and getter


The listener attribute of the ajax component is bound to a method in the personView managed bean that manipulates the button programmatically.
 
public void enableEditBtn() {
   button.setDisabled(false);
}
 

When we now click on a record in the data table, the enableEditBtn method is also executed. It 'overrides' the EL expression #{empty personView.seelctedPerson} with the constant value false. It makes the button enabled on the screen when it gets re-rendered but the most important thing of all, the actionListener and navigation are executed since the value of the disabled property is no longer true during the Apply request values phase.

Conclusion

When a JSF commandButton is initially disabled, there are situations that it doesn't respond to a user click when it is re-enabled. Changing the scope of the bean in the EL expression of the disabled button can solve it. But view scope isn't good for the performance of your application. The use of the binding attribute can be the solution.