Skip to main content

Possible JSF Converter for EJB 3

Posted by jhook on April 20, 2006 at 11:50 PM PDT

One of the nice things about some of this standardization in the JEE 5 stack is the possibility for more cohesive development. I don't think we've even scratched the surface of possibilities here for productive development with EJB 3 and JSF in combination.

One of the things that irks me a lot with web development is the need to pass identifiers around:

  1. Query the DB for a list of Widgets
  2. Render a list of Widgets, generating links to Widget Detail pages, by Widget Id
  3. On Widget Detail page, take the ID passed in the request and query for the specific Widget
  4. Display Widget Detail

So we go around passing things by ID. Unfortunately, JSF still suffers from this to some extent. What would be nice is to just have the Widget Detail page just take in a Widget object, without requiring me to do the translation back and forth by Widget's identifier

With JSF, we have the concept of a Converter, whose job it is to take some object and render it to the page, and then take whatever was rendered to the page and then produce an object. With this capability, don't you think we could automate the Widget->ID->Widget within our application?

<h:selectOneMenu value="#{detailView.widget}" converter="#{widgets.converter}">
  <f:selectItems value="#{listView.allWidgets}">
</h:selectOneMenu>

With the above code, I should be able to use the converter from #{widgets.converter} to do the Widget->ID->Widget transformation for me:

public Converter getConverter() {
    return new EntityConverter(this.entityManager, Widget.class);
}

As a luxury of EJB 3 standardization, I can implement my EntityConverter to figure out which field is the identifier and use it for the Converter.getAsString(...,Widget obj) and then be able to translate and fetch via the EntityManager in Converter.getAsObject(...,String id). With this, we would end up with a rendered output like:

<select id="wid" name="wid">
    <option value="34332">Topper Widget</option>
    <option value="34121">Whopper Widget</option>
    ....
</select>

And, the receiving action would just have a setter that looked like:

public void setWidget(Widget w) {
    this.widget = w;
}

Pseudo code for the EntityConverter:

public class EntityConverter implements Converter {
  private final EntityManager em;
  private final Class type;

  public String getAsString(FacesContext faces, UIComponent c, Object obj) {
    return this.getEntityId(this.type).getId(obj);
  }

  public Object getAsObject(FacesContext faces, UIComponent c, String id) {
    return this.getEntityId(this.type).getObject(id);
  }

  private static class EntityId {
    // abbr

    public String getId(Object obj) {
      return this.idField.get(obj).toString();
    }

    public Object getObject(String id) {
       Object obj = ConvertUtils.convert(id, this.idField.getType());
       return this.em.find(this.type, obj);
    }
  }

}

Thinking about security, we could implement global encoding of identifiers. I don't know about you, but a lot of times when writing web apps, you can either pass identifiers 'clear text' and then do authorization on each succeeding request, or 'encode' the identifiers up front which will guarantee a legit acceptance on a succeeding request.

<select id="wid" name="wid">
    <option value="A4FB39">Topper Widget</option>
    <option value="ABBE92">Whopper Widget</option>
    ....
</select>

So if we are able to look at the JCA API and setup some kind of secret key, per session, I can use the passed FacesContext to check the session for the key and use a simple crypto algorithm to encode/decode identifiers, all within that one SecureEntityConverter. For performance, you could include some kind of caching mechanism at the same scope as the secret key to quickly translate identifiers back and forth.

JSF-wise for performance, the EntityConverter.getAsString(..) is cheap and would be used when rendering lots of widgets to the client. The more expensive fetching by EntityConverter.getAsObject(..) would only be done for the parameter actually passed in the request-- which any solution/framework would have to do. Even in a niche condition, the collection of Widgets would have already been fetched and Persistence implementations would already have a transactional cache over finding a widget by id.

If someone were to implement this, I think it would be quite the gem in JSF's capabilities and basically 'automate' the constant flip flopping we have to do to accomodate client/server communication.