Pages

Saturday, February 25, 2012

SWF Conversion and Formatting

One of properties that I'm binding in one of the views is an UUID. Spring doesn't have default UUID-to-String converter, so I had to write my own. Writing one was easy. Hooking it up to SWF, although SWF does use Spring 3 Type conversion and formatting (they switched to it from their own internal mechanism), was not straightforward.

I went with annotation-based converter, although, in this particular case, type-based would probably be better.

First, I created UUIDFormat annotation and UUIDFormatter (implements org.springframework.format.Formatter<UUID>) that does actual parsing/formatting. Next, UUIDFormatAnnotationFormatterFactory (implementing org.springframework.format.AnnotationFormatterFactory<UUIDFormat>) that maps annotation to formatter.
Next step is to register this formatter with Spring. In Spring MVC an instance of a org.springframework.format.support.FormattingConversionService is created automatically through the custom MVC namespace (when you use <mvc:annotation-driven/>). What we need to do is to create service bean and pass it to the MVC, like this: <mvc:annotation-driven conversion-service="conversionService"/>. In this case I decided not to go with custom service implementation and used default one: org.springframework.format.support.FormattingConversionServiceFactoryBean. Traditionally it is recommended to use installFormatters property of this bean to register your customer formatters , but it looks like it has been deprecated in 3.1. The new recommended way to do it is through formatterRegistrars property that takes a set of FormatterRegistrar implementations.
So we create our own UUIDFormatterRegistrar. Implementation is simple, override registerFormatters method:


@Override
public void registerFormatters(FormatterRegistry registry) {
    registry
        .addFormatterForFieldAnnotation(new UUIDFormatAnnotationFormatterFactory());
}

There you can also register you converters, formatters for field type, etc.

Once that done, pass your registrar to the conversion service:



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

<bean id="conversionService" 
    class="org.springframework.format.support.FormattingConversionServiceFactoryBean"
    p:formatterRegistrars-ref="formatterRegistrars" />
 
<util:set id="formatterRegistrars">
    <bean class="net.koderllc.apparel.spring.UUIDFormatterRegistrar"/>
</util:set>

Spring is now aware about your custom formatters.  Almost there.
SWF creates its own version of conversion service. We need to overwrite this and make SWF conversion service aware about Spring conversion service (the logic of this eludes me, but I might be missing something).



<webflow:flow-builder-services id="flowBuilderServices"
    view-factory-creator="viewFactoryCreator" conversion-service="flowConversionService" />

<bean id="flowConversionService" 
  class="org.springframework.binding.convert.service.DefaultConversionService">
    <constructor-arg ref="conversionService"/>
  </bean>


And that's how we make SWF aware of your custom formatters.

I can't guarantee that this is the best way to do it, but that works for me. If anyone knows a way with less configuration involved - I'd be happy to hear it.

No comments: