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.

Saturday, February 18, 2012

Spring WebFlow Validation part 3 ("Duh")

"Duh" moment...

I didn't have to specify flowScope prefix in view-state model name. form-errors are binding now. Validators work as well.

Another thing that I came across: binding errors will prevent navigation, so add bind="false" to your transition node - validation="false" is not enough if you want this link to work always (i.e. Cancel link).

Spring WebFlow Validation part 2

Using JSR-303 validation model would be nice, but it appears that SWF2.3 doesn't support validation groups, which are essential in a wizard-like interface I'm implementing (JIRA SWF-1453).
I think that I'm having all those validation problems because my model is flow bound (I'm using flowScope prefix for model name). For example spring:hasBindingErrors only works when name is "flowScope.userData".
What's more interesting in
<view-state id="stateOne" model="flowScope.userData">
...
</view-state>

<form:form commandName="userData" method="POST">
  <form:input path="data.value"/>
  <form:errors path="data.value"/>
</form:form>
form:input works fine. Only form:errors refuses to bind.
I'm gonna keep poking it until I figure out what's going on.

Thursday, February 16, 2012

Spring WebFlow validation

Still trying to get a grasp on WebFlow validations. I'm obviously missing something here.

Validator: WebFlow can't find it. Period.
Explicit bindgings: kinda works, throws typeMismatch on empty field (all my properties are BigDecimal), still can't figure out how to display errors.

Model validation methods: also kinda works, throws both typeMismatch and requiredValue (my custom error) on empty fields. Also can't figure out how to display errors.

I'm thinking about trying Wicket for a presentation layer.

Update: Problem solved

Wednesday, February 15, 2012

Cascading Apache Tiles

Today's note: when nesting Apache tiles 2+ levels deep - don't forget to add "cascade" attribute to the top-level put-attribute node, otherwise you might spend quite a few minutes checking the spelling of the said attribute and not understanding why it wasn't found.

Adventures in programming

I decided to dust off this blog and start documenting my work on a project that I'm doing in my free time, just to keep track of things I stumble upon. I can't guarantee regular updates, but then again - no one's reading it anyway, so not like it's a great loss.

Thursday, February 11, 2010

Recruitment Fail

"If you're not interested maybe you know someone who would be. Please let me know if you or someone you know is interested and I'll send you the job description." - from the unsolicited recruiting email...
Three words - WTF?!!!