DigitalJoel

2010/01/11

JSF 2 Custom Converter

Filed under: development, facelets, GAE, java, JSF — Tags: , , , — digitaljoel @ 9:50 pm

I ran into a very strange problem in my little Google App Engine application. I’m using JSF 2 as the presentation layer. I’m using Google’s low level api for accessing the data store and have written some little utilities to convert from my objects to Google’s Entity objects.

I thought I had the mapping working OK, but had only tried setting simple String properties on my data objects. I finally tried to map a simple association, where one object would have a list of keys of other objects. In my case I have Tables. Each table can be associated with zero or more Genres. So, my Table data object has a List of Keys for the genres that it is a part of, like this:

public class Table
        extends DataObjectAbstract
        implements Serializable
{
    @Persistent
    private List<Key> genres;

    // other private attributes here

    public List<Key> getGenres()
    {
        return genres;
    }

    public void setGenres( List<Key> genres )
    {
        this.genres = genres;
    }

    // other public getters and setters and other methods here
}

You can safely ignore DataObjectAbstract and the @Persistent annotation. They aren’t part of this blog post.

The strange behavior I was seeing was that when I would get the genre list from the data store, it would be a List<String> instead of the List<Key> I was expecting. After some debugging, I finally discovered that I was setting it as a List<String> because Strings are what I was getting from my JSF page, and thanks to Java’s type erasure with generics, my code didn’t know any better. So, the solution is a custom converter, which will allow me to pass the Keys to and from the browser.

Fortunately, in JSF 2, custom converters are even easier than they are in JSF 1.x. Here’s my simple converter class for the com.google.appengine.api.datastore.Key class. Making this even more simple is the fact that Google offers a KeyFactory that takes care of giving a URL safe representation of their Key object.

package jota.soc.ui.converter;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;

/**
 * Converter for Google Key.
 * @author Joel.Weight
 */
@FacesConverter( value="keyConverter" )
public class KeyConverter implements Converter {

    /**
     * converts the String representation of the key back to the Object
     */
    public Object getAsObject( FacesContext context, UIComponent component,
                               String value )
    {
        // will throw new IllegalArgumentException if it can't parse.
        return KeyFactory.stringToKey( value );
    }

    /**
     * converts the Key object into its String representation.
     */
    public String getAsString( FacesContext context, UIComponent component,
                               Object value )
    {
        if ( value instanceof Key )
        {
            return KeyFactory.keyToString( (Key)value );
        }
        else
        {
            throw new IllegalArgumentException( "Cannot convert non-key object in KeyConverter" );
        }
    }
}

With the @FacesConverter annotation, you don’t have to register the converter in faces-config or anything. You can simply reference it using the id.

Here’s the code that uses the converter.

<h:selectManyListbox id="genresBox" value="#{createTable.table.genres}">
    <f:converter converterId="keyConverter" />
    <f:selectItems value="#{createTable.genres}"
        var="item"
        itemValue="#{item.key}"
        itemLabel="#{item.name}" />
</h:selectManyListbox>

And just like that, I’m storing Keys in the data store instead of Strings.

Advertisements

2 Comments »

  1. Is it possible to use multiple validators with a custom converter?

    Comment by Vance — 2012/11/11 @ 7:51 pm

    • I’m not sure I understand the question since there is no validation in this post. It’s been 2 years since I touched JSF so I’m not going to be up to snuff on all the latest features, but I’m not sure custom conversion and validation have much overlap so I don’t see how one would really affect the other.

      Comment by digitaljoel — 2012/11/14 @ 9:33 am


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.