Sunday, August 14, 2011

Rendering a Java Map using JSF DataTable

Recently, I had a requirement to render a java map using JSF datatable. Since the map was modified by several threads at the same time, I decided to use ConcurrentMap. When it came to rendering this map, I did a quick google to find out what kind of strategy developers were using to render a java map.

I found out that many developers were rendering a java map using two calls. First, they would return the map itself and then they would return a list of keys using a list. Here is a sample bean source code.

.
.
public Map<String,Employee> getManagers() {
   return managerService.getManagers();
}
public List<String> getRegions() {
   List<String> regions = new ArrayList<String>();
   regions.addAll(managerService.getManagers().keySet());
   return regions;
}
.
.

And the corresponding JSF script.
.
.
<rich:dataTable value="#{managerBean.regions}" var="region" >
   <rich:column>
      <h:outputText value="#{managerBean.managers[region].id}"/>
   </rich:column>
</rich:dataTable>
.
.
The problem with this strategy is that if the map changes after I read the keys (using getRegions()) then I may get an error because the key/value pair may not exist in the map.

Moreover, I wanted the bean to return all the map key/value pairs in a single call. After doing a little research on DataTable and good old java collections library, I came up with this strategy and it works great. The trick is to return a list consisting of map's entry objects that will hold the key/value pair. Here is the sample code:

.
.
public List<Map.Entry<String, Employee>> getManagers() {
   Set<Map.Entry<String, Employee>> managerSet = managerService.getAllManagers().entrySet();
   return new ArrayList<Map.Entry<String, Employee>>(managerSet);
  }
.
.

The corresponding JSF script uses this map entry object to get both the key as well as the value object. This value object can then be used to get the POJO member variables.

.
.
<h:dataTable value="#{managerBean.managers}" var="managerEntry">
  <h:column>
    <h:outputText value="#{managerEntry.key}" />
  </h:column>
 
  <h:column>
    <h:outputText value="#{managerEntry.value.id}" />
  </h:column>
.
.

Download Project
You can download my project from google code. SVN URL is http://ingenious-camel.googlecode.com/svn/trunk/JSFRenderMapDataTable