Thursday, August 7, 2014

High time to standardize Java EE converters?

A common task when developing Java EE applications is that of converting data. In JSF we convert objects to a string representation for rendering it inside an (HTML) response, and convert it back to an object after a postback. In JPA we convert objects from and to types known by our database, in JAX-RS we convert request parameters strings into objects etc etc.

So given the pervasiveness of this task, is there any common converter type or mechanism in the Java EE platform?

Unfortunately it appears such a common converter type is not there. While rather similar in nature, many specs in Java EE define their very own converter type. Below we take a look at the various converter types that are currently in use by the platform.

JSF

One of the earlier converter types in the Java EE platform is contributed by JSF. This converter type is able to convert from String to Object and the other way around. Because it pre-dates Java SE 5 it doesn't use a generic type parameter. While its name and methods are very general, the signature of both methods takes two JSF specific types. These specific types however are rarely if ever needed for the actual conversion, but are typically used to provide feedback to the user after validation has failed.

The main API class looks as follows:

public interface Converter {
    Object getAsObject(FacesContext context, UIComponent component, String value);
    String getAsString(FacesContext context, UIComponent component, Object value);
}
See: javax.faces.convert.Converter

JAX-RS

JAX-RS too defines its very own converter type; ParamConverter. Just like the JSF Converter it's able to convert from a String to any Java Object, but this time there is a generic type parameter in the interface. There's also a method defined to convert the Object back into a String, but this one is curiously reserved for future use.

The main API class looks as follows:

public interface ParamConverter<T> {
    T fromString(String value);
    String toString(T value);
}
See: javax.w.rs.ext.ParamConverter

JPA

One of the most flexible converters in terms of its interface is the JPA converter AttributeConverter. This one is able to convert between any two types in both directions as denoted by 2 generic type parameters. The naming of the converter methods are very specific though.

The main API class looks as follows:

public interface AttributeConverter<X,Y> {
    Y convertToDatabaseColumn (X attribute);
    X convertToEntityAttribute (Y dbData);
}
See: javax.persistence.AttributeConverter

WebSocket

WebSocket has its own converter as well. Architecturally they are a bit different. In contrast with the above shown converters, WebSocket defines separate interfaces for both directions of the conversion whereas the other specs just put two methods in the same type. WebSocket also defines separate interfaces for one of the several supported target types, whereas the other converters support either String or an Object/generic type parameter.

The two supported target types are String and ByteBuffer, with each having a variant where the converter doesn't provide the converted value via a return value, but writes it to a Writer instance that's passed into the converter method as an extra parameter.

Another thing that sets the WebSocket converters apart from the other Java EE converters is that instances have an init and destroy method and are guaranteed to be used by one thread at a time only.

The String to Object API classes look as follows:

public static interface Decoder.Text<T> extends Decoder {
    T decode(String s) throws DecodeException;
    boolean willDecode(String s);
}
public static interface Encoder.Text<T> extends Encoder {
    String encode(T object) throws EncodeException;
}
See: javax.websocket.Decoder.Text See: javax.websocket.Encoder.Text

PropertyEditor (Java SE)

Java SE actually has a universal converter API as well, namely the PropertyEditor. This API converts Objects from and to String, just as the JSF converter. As demonstrated before this type of converter is often used in Java EE code as well.

A PropertyEditor converter is almost always registered globally and inherently stateful. You first set a source value on an instance and then call another method to get the converted value. Remarkable for this converter type is that it contains lots of unrelated methods, including a method specific for painting in an AWT environment: paintValue(Graphics gfx, Rectangle box). This highly unfocused set of functionality makes the PropertyEditor a less than ideal converter for general usage, but in most cases the nonsense methods can simply be ignored and the ubiquitous availability in Java SE is of course a big plus.

The main API class and main conversion methods look as follows:

public interface PropertyEditor {
    // String to Object
    void setAsText(String text) throws IllegalArgumentException;
    Object getValue();
    // Object to String
    void setValue(Object value);
    String getAsText();
    // Tons of other useless methods omitted
}
See: java.beans.PropertyEditor

Other

There are some specs that use a more implicit notion of conversion and could take advantage of a platform conversion API if there happened to be one. This includes remote EJB and JMS. Both are capable of transferring objects in binary form using what is essentially also a kind of conversion API: Java SE serialization. Finally JAXB has a number of converters as well, but they are build in and only defined for a finite amount of types.

Conclusion

We've seen that there are quite a number of APIs available in Java EE as well as Java SE that deal with conversion. The APIs we looked at differ somewhat in capabilities, and use different terminology for what are essentially similar concepts. The platform as a whole would certainly benefit from having a single unified conversion API; this could eventually somewhat reduce the size of individual specs, makes it easier to have a library of converters available and would surely give the Java EE platform a more consistent feel.

Arjan Tijms

4 comments:

  1. Along the configuration JSR, if we decide to define a unified, String based config model, the same topic will raise as well. So we might go an "extra mile", and design a more flexible and overall reusable API, instead of creating a complete standalone API additionally... tbd in the EG, of course.

    ReplyDelete
  2. Also JAXB (JSR-222) has a converter called XmlAdapter<ValueType, BoundType>.

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. This must not be done for EE alone if it ever happened. You completely forgot the JavaFX converter package: http://docs.oracle.com/javafx/2/api/javafx/util/converter/package-summary.html

    And btw. there's also a UnitConverter though a bit more specialized in JSR 363 http://www.unitsofmeasurement.org/jsr-363/apidocs/javax/measure/UnitConverter.html

    I got feedback from then outgoing EC rep Wayne Carr of Intel 5 years ago shortly after JSR 275 was stopped. He suggested it might be worth at least 2 or more separate JSRs. One about "general purpose type conversion" I think that's more or less along the lines of this discussion ;-)

    ReplyDelete