JSF Select Widgets (a little) Easier

2009/12/17

The implementation of JSF select lists have always confounded me a little. When building your array of SelectItem objects, you are free to put any object as the item’s value. However, simply doing that will lead to confusing / misleading errors. The catch is that you must implement a JSF Converter for the value type in your SelectItem.

I don’t understand why the JSF impl can’t smooth this pattern over. For example, wouldn’t it be enough for the SelectItem values to be uniquely identifiable via a string? Then JSF can simply map them to a the real object stored on the server. Even for a client state saving method, couldn’t JSF serialize the object itself on the client page? Now we are getting somewhere.

The above is really only feasible if your value type is easily transformable to and from a string. If it isn’t, then you are stuck writing comlex serialization / de-serialization routines for any object value that you use in a SelectItem. Why not generalize that? Below is a general-purpose ASCII (and hence web safe) serialization / de-serialization utility.

import com.sun.identity.shared.encode.Base64;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class AsciiSerializer {
  public static String serialize(Object o) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream os = new ObjectOutputStream(baos);
    os.writeObject(o);
    os.flush();
    os.close();

    String es = Base64.encode(baos.toByteArray());
    return es;
  }

  public static Object deserialize(String es) throws IOException, ClassNotFoundException {
    byte[] ba = Base64.decode(es);
    ByteArrayInputStream bais = new ByteArrayInputStream(ba);
    ObjectInputStream is = new ObjectInputStream(bais);

    Object o = is.readObject();
    is.close();

    return o;
  }
}

You still must write and register a converter, but now it is trivial,

public class MyConverter implements Converter {
  public Object getAsObject(FacesContext context, UIComponent component, String value) {
    MyObject mo = AsciiSerializer.deserialize(value);
    return mo;
  }

  public String getAsString(FacesContext context, UIComponent component, Object value) {
    return AsciiSerializer.serialize(value);
  }
}
Advertisements

Java EE Got it Wrong

2009/11/17

After working on two large-scale, cross contains / platform, web application projects, I’ve come to the conclusion that Java EE is fatally flawed.

If someone asked you to state the most important, guiding principle of the Java language, what would you say? You would would probably say “write once, run anywhere.” Indeed, for Java SE this holds true most all of the time. In Java EE however, it is hopelessly broken. The reality is that every web container is slightly different in ways that force developers work around problems, avoid certain features, or even have different code paths. Different code paths in a language that was never designed for it.

Different aspects of Java EE have different levels of stability. JSP works almost the same across all containers. Things like JSF and Java Persistence (JPA) however are hopeless. My most recent experience is writing a web administration console using JSF for the OpenSSO project. OpenSSO claims to support at at least 8 different web containers. Every container required JSF tweaking that ranged from annoying to just plain terrible. Needless to say I had egg on my face to some degree over the choice to use JSF. It was a no-brainer for me. JSF is the chosen MVC framework for Java EE. My mistake.

The only container where JSF “just worked” was Tomcat. Tomcat isn’t a Java EE container at all.Tomcat doesn’t include JSF or any of it’s dependencies. We might be on to something here. Let’s look at another example: Spring. Spring is essentially a lighter-weight, simpler replacement for Java EE. Why is Spring so popular? One reason is that it is an elegant design. But another, perhaps more important reasons is that no matter what container you are developing on, Spring is Spring. It’s the same JAR, the same code, the same implementation. It makes very few assumptions about the capabilities of the underlying container. Another example is Facelets, a light-weight JSP replacment. I use JSF+Facelets on the OpenSSO project, and it has been flawless (the Facelets part). Why? Because I include the Facelets JAR, it is not provided by the system.

What if Java EE had followed the same model? Java EE should have been nothing more than a plugin framework and some base services. Want to use JSF in your project? Just include it from a managed, networked Java EE module repository. Include the one, common implementation of JSF. Include the approved version for your level of Java EE. If you support Java EE 5, you get JSF version X, across all web containers.