Enum translations in Spring MVC

I recently came across this requirement but it wasn’t immediately obvious how to implement it. I have an enum in my application which defines the locales supported. For instance you might setup enum values of EN, FR and ES. (if English, French and Spanish are the languages you have translation properties files for) I then wanted to display a dropdown on the user screen with the values of this enum as available languages to select.

First stab
Here is the enum in question and my first go at displaying it.

/**
 * Languages supported with translations for the UI. A user can view the application in one 
 * of these languages.
 * 
 * @author Ben Thurley
 */
public enum SupportedLanguage 
{
  EN, FR, ES;
}

I got this piece of JSP from an answer on Stack Overflow

<form:select path="supportedLanguage">
   <form:options/>
</form:select>

This worked ok except the select dropdown just displayed EN, FR and ES. I needed a way of translating these codes into text for the users current selected language. I did see some solutions suggesting doing this in the controller. You could read all values and create a custom map of options with the right piece of translated text. I didn’t like the idea of this though as it was adding translation logic to the controller when for every other element it’s taken care of by Spring. There had to be another way and as it turned out, there was.

Spring custom formatter
Spring MVC allows us to write our own custom formatters. I just needed one that would take my enum code and format it to the translated string equivalent for the users locale.

This is what I ended up with:

public class SupportedLanguageFormatter implements Formatter<SupportedLanguage>
{

  @Resource
  private MessageSource messageSource;
	
  @Override
  public String print(SupportedLanguage object, Locale locale) 
  {
    // Lookup message translation, no args, default of English
    return messageSource.getMessage("supported.language." + object.name().toLowerCase(), null, "English", locale);
  }

  @Override
  public SupportedLanguage parse(String text, Locale locale) throws ParseException 
  {
    return SupportedLanguage.parse(text);
  }
}

This formatter does a two way translation. The print method takes a SupportedLanguage and looks up the relevant translated String. This is easily located using the injected MessageSource. For the reverse operation the parse method takes in a code from the UI (such as EN or FR) and returns an instance of the SupportedLanguage enum.

I tweaked the enum a bit so the parsing was encapsulated within the SupportedLanguage enum. Here’s the final version.

public enum SupportedLanguage 
{
  EN, FR, ES;

  public static List<SupportedLanguage> list() 
  {
    return Arrays.asList(values());
  }
	
  public static SupportedLanguage parse(String value)
  {
    SupportedLanguage lang = null;
    	
    for(SupportedLanguage test : list())
    {
      if(test.name().equalsIgnoreCase(value))
      {
        lang = test;
    	break;
      }
    }
    	
    return lang;
  }  
}

We then register the new formatter with spring.

  <mvc:annotation-driven conversion-service="conversionService">
  </mvc:annotation-driven>

  <!-- Custom formatters -->
  <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="formatters">
      <set>
        <bean class= "org.languagetool.web.formatter.SupportedLanguageFormatter" />
      </set>
    </property>
  </bean>

Now the SupportedLanguage enum is ready to be used. I did this by registering a ModelAttribute in the controller.

@Controller
@RequestMapping(value="admin/users")
@SessionAttributes({"userForm", "authoritiesList", "supportedLanguagesList"})
public class UserController
{
  ...
  @ModelAttribute("supportedLanguagesList")
  public List<SupportedLanguage> getAllSupportedLanguages() 
  {
    return SupportedLanguage.list();
  }  
  ...
}

This can now be used in the JSP as a set of options, I also added in an empty option if no language is selected on a new user form.

<form:select path="supportedLanguage">
  <spring:message code="option.select" var="selectOptionText" />
  <form:option value="" label="${selectOptionText}" />
  <form:options items="${supportedLanguagesList}" />
</form:select>

That’s almost it, now you just have to remember to add the translations to the relevant properties files.

# Languages
supported.language.en=English
supported.language.es=Spanish
supported.language.fr=French
Advertisements

Initialising DataTables with Twitter Bootstrap and Spring MVC Ajax

In the previous post I introduced handling Ajax requests from DataTables using Spring MVC.
Spring MVC with restful DataTables

This is only one side of the story though as I left out how I was doing the initialisation on the client side. I’m using DataTables with twitter bootstrap so my initialisation is doing two things, one setting up communication with the back end and two adding twitter bootstrap styling.

Continuing with the customer grid example this is my grid.

<table class="table table-striped table-bordered" id="customers">
  <thead>
    <tr> <th>ID</th> <th>Name</th> </tr>
  </thead>
  <tbody></tbody>
</table>
<script type="text/javascript">
	$(document).ready(function() {
	    $('#customers').dataTable( {
	    	"sAjaxSource": "/Root/customers/get",
	    	"bProcessing": true,
	    	"bServerSide": true,
	    	"sPaginationType": "bootstrap",
	    	"oLanguage": {"sLengthMenu": "_MENU_ records per page",
	    				  "sInfo": "Displaying _START_ to _END_ of _TOTAL_ records"},
	        "aoColumns":[
				{"mDataProp":"id"},
				{"mDataProp":"name"}
				]
	    } );
	} );	
</script>

The first three properties set up the Ajax processing, pagination type gives us bootstrap style paging buttons and oLanguage allows us to change the standard labels. When it came to mapping the columns I had to use the aoColumns property and setting the mDataProp value. In most of the examples online you’ll see a PHP script returning the JSON as an array but since we’re using Jackson we’re returning an object. The difference is this.

Array

{
  "aaData": [
    [
      "1",
      "Ben Thurley"
    ],
    [
      "2",
      "Another Person"
    ]
  ]
}

Object

{
  "aaData": [
    {
      "id" : "1",
      "name" : "Ben Thurley"
    },
    {
      "id" : "2",
      "name" : "Another Person"
    }
  ]
}

If you’re returning an array then you give DataTables the index of the property that matches the column, as we’re returning objects we have to give the name of the property that matches the column. We do this by setting the mDataProp property.

As an added bonus DataTables sends back these mDataProp values on the Ajax request which allows us to convert the sort column index into a meaningful column name.

The rest of the twitter bootstrap styling steps can be found on the DataTables site.
http://datatables.net/blog/Twitter_Bootstrap_2

It’s somewhat work in progress but I have also worked this into a JSP tag file so I don’t need to keep writing the same stuff in my JSP’s. It should in theory make it easy to change the tale plugin if required at a later date. I just added the following two tag files into WebRoot/WEB-INF/tags/table.

table.tag

<%@ tag language="java" %>
<%@ attribute name="id" required="true" %>
<%@ attribute name="datasource" required="true" rtexprvalue="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<c:set var="org_languagetool_tags_table_outputmode" value="TABLE" scope="request" />
	<table class="table table-striped table-bordered" id="${id}">
	  <thead>
	    <tr><jsp:doBody /></tr>
	  </thead>
	  <tbody></tbody>
	</table>
<c:set var="org_languagetool_tags_table_outputmode" value="SCRIPT" scope="request" />
<c:set var="org_languagetool_tags_table_firstcolumn" value="TRUE" scope="request" />
	<script type="text/javascript">
		$(document).ready(function() {
		    $('#${id}').dataTable( {
		    	"sAjaxSource": "<c:url value="${datasource}" />",
		    	"bProcessing": true,
		    	"bServerSide": true,
		    	"sPaginationType": "bootstrap",
		    	"oLanguage": {"sLengthMenu": "<spring:message code="table.records.count" />",
		    				  "sInfo": "<spring:message code="table.showing.records" />"},
		        "aoColumns":[
		                   	<jsp:doBody />
		                   	]
		    } );
		} );	
	</script>	

column.tag

<%@ tag language="java" %>
<%@ attribute name="field" required="true" %>
<%@ attribute name="label" required="true" rtexprvalue="true" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<c:choose>
  <c:when test="${org_languagetool_tags_table_outputmode == 'TABLE'}">
    <th><spring:message code="${label}" /></th>
  </c:when>
  <c:when test="${org_languagetool_tags_table_outputmode == 'SCRIPT'}">
    <c:choose>
      <c:when test="${org_languagetool_tags_table_firstcolumn == 'TRUE' }">
        <c:set var="org_languagetool_tags_table_firstcolumn" value="FALSE" scope="request" />
      </c:when>
      <c:otherwise>,</c:otherwise>
    </c:choose>
    {"mDataProp":"${field}"}
  </c:when>
</c:choose>

It’s not required unless you’re putting these in a jar but I also wrote out a tag library descriptor.

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd">
  <tlib-version>1.0</tlib-version>
  <short-name>Table</short-name>
  <uri>http://bthurley.org/tags/table</uri>
  
  <tag-file>
    <name>table</name>
    <path>/WEB-INF/tags/table/table.tag</path>
  </tag-file>  
  
  <tag-file>
    <name>column</name>
    <path>/WEB-INF/tags/table/column.tag</path>
  </tag-file>
 
</taglib>

By using a tld I’ve been able to specify a uri for importing and using the tag. Otherwise you can give the path to import the tag.

<%@ taglib prefix="tab" uri="http://bthurley.org/tags/table" %>
<tab:table id="customers" datasource="/customers/get" >
  <tab:column field="id" label="customers.field.id" />
  <tab:column field="name" label="customers.field.name" />
</tab:table>

I guess I’ll be adding to these tags as time goes on and I think of new things.

Spring MVC with restful DataTables

Up until recently I’ve been using jqGrid for the app I’m working on but I wanted to re-work the UI to make use of twitter bootstrap. I’m not a graphic designer and TB certainly gives you a polished looking app with minimal effort. After some research it seemed that jQuerys DataTables plugin had better UI support for TB than jqGrid. jqGrid is reliant on jQuery UI for its look and feel which as of yet doesn’t have a complete TB theme (though there is work in progress).

If you want to know how to get DataTables displaying in a TB style then the main website has a good walkthrough. It’s really not too difficult. http://datatables.net/blog/Twitter_Bootstrap_2

What was slightly trickier was processing the AJAX requests using Spring MVC. Most of the examples on the site are with PHP where it is quite normal to manually retrieve request parameters. In contrast with Spring MVC the aim is to have a POJO controller and map each parameter on the request to a method parameter. Unfortunately when it comes to sort columns DataTables passes a number to tell you how many columns to sort on and then each column is on the request with an index as part of the parameter name.

You end up needing this kind of thing.

$sOrder = "";
if ( isset( $_GET['iSortCol_0'] ) )
{
  $sOrder = "ORDER BY ";
  for ( $i=0 ; $i<intval( $_GET['iSortingCols'] ) ; $i++ )
  {
    if ( $_GET[ 'bSortable_'.intval($_GET['iSortCol_'.$i]) ] == "true" )
    {
      $sOrder .= "`".$aColumns[ intval( $_GET['iSortCol_'.$i] ) ]."` ". mysql_real_escape_string($_GET['sSortDir_'.$i] ) .", ";
    }
  }
  $sOrder = substr_replace( $sOrder, "", -2 );
  if ( $sOrder == "ORDER BY" )
  {
    $sOrder = "";
  }
}

Now you could do something similar by passing the request object into the method and let your method retrieve the parameters manually but this defeats the object a bit and makes unit testing harder. Instead I decided to create a class to model the DataTables parameters and get Spring to populate it automatically. It’s then possible to pass this single object down through the layers to the place that needs to do the DB query. This makes the parameter lists much shorter and easier to maintain since you can simply add an attribute to the containing object rather than having to visit every method signature.

My object to hold the basic paging criteria is a simple POJO. I’ve made it immutable because you shouldn’t be able to override the inputs that the user has set.

public final class PagingCriteria
{
  private final Integer displayStart;
  private final Integer displaySize;
  private final List<SortField> sortFields;
  private final Integer pageNumber;

  public PagingCriteria(Integer displayStart, Integer displaySize, Integer pageNumber, List<SortField> sortFields)
  {
    this.displayStart = displayStart;
    this.displaySize = displaySize;
    this.pageNumber = pageNumber;
    this.sortFields = sortFields;
  }

  public Integer getDisplayStart() {
    return displayStart;
  }

  public Integer getDisplaySize() {
    return displaySize;
  }

  public List<SortField> getSortFields()
  {
    return Collections.unmodifiableList(sortFields);
  }

  public Integer getPageNumber()
  {
    return pageNumber;
  }

}

I’ve also extracted the concept of a sort field and direction into a further class with an enum representing sort directions.

public final class SortField 
{
  private final String field;
  private final SortDirection direction;
	
  public SortField(String field, SortDirection direction)
  {
    this.field = field;
    this.direction = direction;
  }
	
  public SortField(String field, String direction)
  {
    this.field = field;
    this.direction = SortDirection.valueOfCaseInsensitive(direction);
  }

  public String getField() {
    return field;
  }

  public SortDirection getDirection() {
    return direction;
  }

}
public enum SortDirection 
{
  ASC("asc"), DESC("desc");
	
  private String direction;
  private static final Logger logger = LoggerFactory.getLogger(SortDirection.class);
	
  private SortDirection(String direction)
  {
    this.direction = direction;
  }
	
  public String getDirection()
  {
    return this.direction;
  }
	
  public static SortDirection valueOfCaseInsensitive(String value)
  {
    String valueUpper = value.toUpperCase();
    SortDirection sd = SortDirection.valueOf(valueUpper);
    logger.debug("SortDirection converted "+value+" to "+sd);
    return sd;
  }
}

The PagingCriteria object is passed into my Spring MVC controller something like this.

@Controller
@RequestMapping(value="customers")
public class CustomerController
{

  @Autowired
  private CustomerService customerService;

  @RequestMapping(value="/get", method=RequestMethod.GET)
  public @ResponseBody WebResultSet<Customer> getCustomers(@TableParam PagingCriteria criteria)
  {
    ResultSet<Customer> customers = this.customerService.getCustomers(criteria);
    return ControllerUtils.getWebResultSet(criteria, customers);
  }
...

Ignore the return type for the moment, we’ll come back to that. This is mapping all GET requests to /customers/get to the getCustomers method. It takes in a single parameter for the paging criteria which has been annotated with @TableParam. This is a custom annotation created for populating PagingCriteria with parameters on the request. The annotation class file itself is pretty straightforward.

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TableParam
{

}

So it’s just a simple marker. We’re using it to annotate method PARAMETER’s and we want to retain this annotation at RUNTIME. Next we need a class that can resolve this annotation and return our PagingCriteria object.

public class TableParamArgumentResolver implements WebArgumentResolver
{

  private static final String S_ECHO = "sEcho";
  private static final String I_DISPLAY_START = "iDisplayStart";
  private static final String I_DISPLAY_LENGTH = "iDisplayLength";
  private static final String I_SORTING_COLS = "iSortingCols";

  private static final String I_SORT_COLS = "iSortCol_";
  private static final String S_SORT_DIR = "sSortDir_";
  private static final String S_DATA_PROP = "mDataProp_";

  public Object resolveArgument(MethodParameter param, NativeWebRequest request) throws Exception
  {
    TableParam tableParamAnnotation = param.getParameterAnnotation(TableParam.class);

    if (tableParamAnnotation!=null)
    {
      HttpServletRequest httpRequest = (HttpServletRequest) request.getNativeRequest();

      String sEcho = httpRequest.getParameter(S_ECHO);
      String sDisplayStart = httpRequest.getParameter(I_DISPLAY_START);
      String sDisplayLength = httpRequest.getParameter(I_DISPLAY_LENGTH);
      String sSortingCols = httpRequest.getParameter(I_SORTING_COLS);

      Integer iEcho = Integer.parseInt(sEcho);
      Integer iDisplayStart = Integer.parseInt(sDisplayStart);
      Integer iDisplayLength = Integer.parseInt(sDisplayLength);
      Integer iSortingCols = Integer.parseInt(sSortingCols);

      List sortFields = new ArrayList();
      for(int colCount=0; colCount<iSortingCols; colCount++)
      {
        String sSortCol = httpRequest.getParameter(I_SORT_COLS+colCount);
        String sSortDir = httpRequest.getParameter(S_SORT_DIR+colCount);
        String sColName = httpRequest.getParameter(S_DATA_PROP+sSortCol);
        sortFields.add(new SortField(sColName, sSortDir));
      }

      PagingCriteria pc = new PagingCriteria(iDisplayStart, iDisplayLength, iEcho, sortFields);

      return pc;
    }

    return WebArgumentResolver.UNRESOLVED;
  }
}

All we do is implement WebArgumentResolver which has a single method resolveArgument. One of the parameters to this method is the request which allows us to retrieve the named request parameters we need. DataTables just passes a column index for sorting but we can use this index to lookup the column name too if mDataProp is used to initialise DataTables.

It’s very important to check first that the annotation being processed is our TableParam and if not to return WebArgumentResolver.UNRESOLVED. The first time through Spring calls each resolver in turn until it finds one that handles that annotation. This is cached and from then on it will always call that resolver for that annotation.

All that’s left now is to register our resolver with spring in the servlet context. This is much easier in Spring 3.1 because of the extra parameters you can now pass to mvc:annotation-driven.

  <mvc:annotation-driven>
    <mvc:argument-resolvers>
      <bean class="org.languagetool.web.utils.TableParamArgumentResolver" />
    </mvc:argument-resolvers>
  </mvc:annotation-driven>

That’s pretty much it. Now when a request comes in to getCustomers(), Spring  sees the @TableParam annotation and calls our custom resolver. This wings around the request picking up all the DataTables parameters and puts them in a PagingCriteria which it then returns. The great thing is once you’ve set this up you can easily reuse it throughout your controllers.

But what about the return type on the controller? If we take another look at the controller method.

@RequestMapping(value="/get", method=RequestMethod.GET)
public @ResponseBody WebResultSet<Customer> getCustomers(@TableParam PagingCriteria criteria)
{
  ResultSet<Customer> customers = this.customerService.getCustomers(criteria);
  return ControllerUtils.getWebResultSet(criteria, customers);
}

There are a couple of things here. The service call is returning a ResultSet object but the controller returns a WebResultSet. This has been annotated with @ResponseBody so if the request object has a request type of JSON then this object will be automatically parsed and returned as JSON. Likewise a content type of XML would return XML assuming the correct dependencies are on the classpath (Jackson and JAXB respectively).

My services are returning a ResultSet object which is similar to PagingCriteria in that it simply wraps the returning data. It’s basically a collection but with extra bits needed to display in the grid such as a record count. The ResultSet holds a collection of DomainObject’s, this is my layer super type for JPA entities which I will be posting about at a later date.

public final class ResultSet<T extends DomainObject>
{
  private final List<T> rows;
  private final Long totalDisplayRecords;
  private final Long totalRecords;

  public ResultSet(List<T> rows, Long totalRecords, Long totalDisplayRecords)
  {
    this.rows = rows;
    this.totalRecords = totalRecords;
    this.totalDisplayRecords = totalDisplayRecords;
  }

  public List<T> getRows()
  {
    return Collections.unmodifiableList(rows);
  }

  public Long getTotalDisplayRecords()
  {
    return totalDisplayRecords;
  }

  public Long getTotalRecords()
  {
    return totalRecords;
  }
}

DataTables itself though will be looking for these parameters to have specific names. It also must have the sEcho parameter from the request sent back. This is a “draw count” which datatables increments each time it makes a request. (Thanks to Sean in the comments for explaining this) Luckily we retrieved sEcho behind the scenes into our PagingCriteria so we use the static ControllerUtils.getWebResultSet() method to build us a DataTables friendly resultset.

public static <T extends DomainObject> WebResultSet<T> getWebResultSet(PagingCriteria pc, ResultSet<T> rs)
{
  return new DataTablesResultSet<T>(pc, rs);
}
public class DataTablesResultSet<T extends DomainObject>  implements WebResultSet<T>
{
  private final Integer sEcho;
  private final Long iTotalRecords;
  private final Long iTotalDisplayRecords;
  private final List<T> aaData;

  public DataTablesResultSet(PagingCriteria pc, ResultSet<T> rs)
  {
    this.sEcho = pc.getPageNumber();
    this.aaData = rs.getRows();
    this.iTotalRecords = rs.getTotalRecords();
    this.iTotalDisplayRecords = rs.getTotalRecords();
  }

  public Integer getsEcho() {
    return sEcho;
  }

  public Long getiTotalRecords() {
    return iTotalRecords;
  }

  public Long getiTotalDisplayRecords() {
    return iTotalDisplayRecords;
  }

  public List<T> getAaData() {
    return Collections.unmodifiableList(aaData);
  }
}

So WebResultSet is simply a marker interface that’s used as a return type on the controllers. We then determine in ControllerUtils.getWebResultSet that it’s a DataTablesResultSet we want. In theory we can now tweak both the inputs and outputs from DataTables or any other grid we choose without having to change the controllers or services.  We’ve essentially removed the controllers dependency on the JavaScript grid implementation.

The next step is to wrap the view layer in custom JSP tags

Accessing generic type info at runtime

I came across this problem when trying to create a generic data access layer supertype. I wanted an abstract DAO class that all my DAO’s would extend and automatically get the basic operations for free. One of the operations I wanted was a select query which I could add some basic sorting and paging to based on parameters passed in. To create the basic query I needed a way to get the table name, in theory this was available as a generic type argument but accessing it was a bit tricky…

This is what I came up with.

import org.springframework.core.GenericTypeResolver;
public abstract class AbstractHibernateDao<T extends DomainObject> implements DataAccessObject<T>
{
  @Autowired private SessionFactory sessionFactory;
  private final Class<T> genericType;
  private final String RECORD_COUNT_HQL;
  private final String FIND_ALL_HQL;
  public AbstractHibernateDao()
  {
    this.genericType = (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), AbstractHibernateDao.class);
    this.RECORD_COUNT_HQL = "select count(*) from " + this.genericType.getName();
    this.FIND_ALL_HQL = "from " + this.genericType.getName() + " t ";
  }
....

Using springs GenericTypeResolver it was really easy to get the class name and generate the queries. This does of course assume that my domain objects have the same name as their tables. If you have more than one type argument e.g. <T extends DomainObject, PK extends PrimaryKey> then resolveTypeArguments can be used which returns Class[].

If you can’t use spring then it can also be done using plain Java.  Arjan explains this in his blog here:

http://blog.xebia.com/2009/02/07/acessing-generic-types-at-runtime-in-java/

JPA – JPQL to join an entity not mapped

Today I came across a bit of a problem converting an old SQL select statement into JPQL. Essentially the problem was that the original query used a join but my two entities weren’t mapped to each other.

As an example, lets say we have the following two entities.

@Entity
@Table(name="Animal", schema="test")
public class Animal
{
    @Id @Column(name="ID", nullable=false)
    private Long animalID;

    @Column(name="COLOUR")
    private String colour;
    ...
}

Let’s say the parent entity is a generic Animal class. In this example we also have a Fish class which captures any extra attributes that a fish has in addition to an animal. Maybe the number of fins for example.

@Entity
@Table(name="Fish", schema="test")
public class Fish
{
    @Id @Column(name="ID", nullable=false)
    private Long animalID;

    @Column(name="NUMOFFINS")
    private Integer numberOfFins;
    ...
}

Given these entities, with no inheritance relationship or JPA mappings, I needed to convert the following SQL select statement to select all fish that are grey in colour.

SELECT f.* FROM test.Fish f INNER JOIN test.Animal a ON f.animalID = a.animalID WHERE a.COLOUR = "Grey"

My first stab at converting this to JPQL looked like this:

SELECT f FROM Fish f INNER JOIN Animal a WHERE f.animalID = a.animalID AND a.COLOUR = "Grey"

This is where I hit problems. In JPQL you’re not joining tables but entities and as such you can’t join two entities that don’t have a mapped relationship. Idealistically I would want to make Fish subclass Animal and map the relationship properly but for now this is out of scope. I managed to solve this by replacing the join with a sub select.

SELECT f FROM Fish f WHERE f.animalID in (SELECT a.animalID FROM Animal a WHERE a.COLOUR = "Grey")

JPA doesn’t seem to mind this and you could even change it to use exists.

SELECT f FROM Fish f WHERE EXISTS (SELECT a FROM Animal a WHERE f.animalID = a.animalID AND a.COLOUR = "Grey")

It takes some practice to stop thinking of tables and start thinking of entities!

jqGrid RESTful URL formatter

jQuery seems to have become ubiquitous these days and if you want a scrolling, paging, dynamic grid then jqGrid is an excellent plugin. I’ve been using it in my project with a RESTful JSON web service to return the grid content and I needed a way to format the ID column into a URL. First I had a look at the documentation to see what jqGrid has out of the box.

There is a formatter called ‘link’ or ‘showLink’ but it didn’t quite work as I wanted as it forces you to use a URL of the form site.com/page?id=1. My application is using RESTful URL’s so I wanted something more like site.com/page/1. This left me with a couple of options.

  1. Format the URL on the server and return it from the JSON web service
  2. Create a custom formatter

For me 1 was out of the question because this was a formatting issue that belonged in the view layer. My JSON is being returned as a POJO from a spring controller. The conversion to JSON is handled automatically by Jackson. So you can see I didn’t really want this URL formatting going on in the controller, or worse the model layers.

This is the custom formatter I created for this purpose.

jQuery.extend($.fn.fmatter , {
RESTfulLink : function(cellvalue, options, rowdata)
{
var opts = options.colModel.formatoptions.url;
if(typeof opts == 'string')
{
return '<a href="'+opts + cellvalue+'">'+cellvalue+'</a>';
}else{
return cellvalue;
}
}
});

The first line allows us to create a formatter by extending jqGrid. It is also possible to create a formatter as a standalone function but I can’t see any reason why you would need to do this. The rest is standard JavaScript defining a formatter called RESTfulLink. The parameters are the standard parameters required for a formatter function. This can all be found on the jqGrid wiki.

The interesting part that isn’t mentioned in the documentation, or anywhere else I could find, is how to access a formatter option. This might make more sense once we’ve seen how you use this formatter.

<script>
  jQuery("#grid_id").jqGrid({
  ...
     colModel: [
      ...
     {name:'id', index:'id', width:60, align:"center", editable: true, formatter:'RESTfulLink', formatoptions:{url:'site.com/page/'}},
      ... ]
   ... });

We select the formatter in the same way as any of the standard ones. Note that if we’d done this is a standalone function instead of extending jqGrid then the quotes would have been dropped. E.G. formatter:RESTfulLink. The data we need to create the link has been passed in using the formatoptions object. I’ve created a custom parameter called url but since JavaScript is dynamically typed you could pass in whatever you want.

If you look back at the custom formatter you should see the url parameter being used to construct the URL by appending the contents of the ID column to whatever the URL is. I couldn’t find this documented anywhere so I ended up debugging my function and inspecting the parameters passed in. Otherwise there’s no way I would have guessed it would be options.colModel.formatoptions.url!

So that’s my first proper post, I hope it makes sense. Please leave a comment if you liked it or to suggest an improvement.

Thanks

Ben

Introduction

Hello World!

This is my first post on this blog, or any blog, so I thought I’d do a brief introduction and set out my aims. My name is Ben and I work as a software developer based in the UK. I recently started a small project to write a web app I could use to learn a bit of French. I use java in my day job so I was looking to try out some different frameworks that may be of use at work. (Spring, hibernate, that kind of thing)

As a programmer you’re constantly solving small problems but once you get something working it’s all too easy to move on and forget. Even with version control systems it’s not always easy to find solutions from the past. If the codebase is large you have to remember what to search on, sometimes you can’t even access the code any more. My aim then is to document these solutions so I have a record to refer back to. I decided to document this as a blog so that others could potentially read and use the same solutions.

I’ve decided not to call this a java blog specifically because I may end up blogging about other languages or tools I come across.

Thanks

Ben