Spring 3.0 Annotated Formatter Not Working
I am attempting to leverage the new annotated formatter support in Spring 3+. I am using Spring 3.0.6 and my custom formatters are not being appliedin my JSP page even though I register them and use spring:bind tag.
My application context is configured as follows:
Code:
<mvc:annotation-driven conversion-service="formattingFactory" />
<bean id="formattingFactory" class="com.rubyrentals.spring.formatters.FormattingFactory" />
The FormattingFactory class:
Code:
public class FormattingFactory extends FormattingConversionServiceFactoryBean {
@Override
protected void installFormatters(FormatterRegistry registry) {
registry.addFormatterForFieldAnnotation(new MonthNameFormatterFactory());
registry.addFormatterForFieldAnnotation(new ImpliedCurrencyFormatterFactory());
super.installFormatters(registry);
}
}
The MonthName formatter:
Code:
public class MonthNameFormatter implements Formatter<Number> {
@Override
public String print(Number value, Locale locale) {
DateTime date = new DateTime();
return date.withMonthOfYear((Integer) value).monthOfYear().getAsText();
}
@Override
public Number parse(String value, Locale locale) throws ParseException {
return DateTimeFormat
.forPattern("MMMM")
.withLocale(locale)
.parseDateTime(value)
.getMonthOfYear();
}
}
The MonthNameFormat attribute:
Code:
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MonthNameFormat {
}
The MonthNameFormatterFactory class:
Code:
/*
* Configures MonthNameFormat annotation for use in spring.
*/
public final class MonthNameFormatterFactory implements AnnotationFormatterFactory<MonthNameFormat> {
@Override
public Set<Class<?>> getFieldTypes() {
HashSet<Class<?>> set = new HashSet<Class<?>>();
set.add(Short.class);
set.add(Integer.class);
set.add(Long.class);
set.add(BigInteger.class);
return set;
}
@Override
public Parser<Number> getParser(MonthNameFormat attribute, Class<?> clazz) {
return new MonthNameFormatter();
}
@Override
public Printer<Number> getPrinter(MonthNameFormat arg0, Class<?> arg1) {
return new MonthNameFormatter();
}
}
The following unit tests work, but the formatter is not picked up through the JSP page even though I use spring:bind tag.
MonthNameFormatterTests
Code:
public class MonthNameFormatterTests {
@Test
public void BasicFormatterTest() {
Integer month = 1;
String monthName = "January";
try {
MonthNameFormatter formatter = new MonthNameFormatter();
Assert.assertEquals(month, formatter.parse(monthName, Locale.getDefault()));
Assert.assertEquals(monthName, formatter.print(month, Locale.getDefault()));
}
catch(Exception e) {
Assert.fail("Failed to format value!\n" + e.getMessage());
}
}
}
MonthNameFormatterFactoryTests
Code:
public class MonthNameFormatterFactoryTests extends AbstractSpringTest {
/*
* Sample class for testing
*/
public class MonthNameData {
@MonthNameFormat
private Integer month;
public Integer getMonth() {
return this.month;
}
public void setMonth(Integer month) {
this.month = month;
}
public MonthNameData(Integer month) {
this.setMonth(month);
}
}
@Test
public void FactoryCanConvertValueToString() {
try {
MonthNameData data = new MonthNameData(DateTimeConstants.JANUARY);
PropertyDescriptor prop = new PropertyDescriptor("month", MonthNameData.class);
MethodParameter param = MethodParameter.forMethodOrConstructor(MonthNameData.class.getMethod("getMonth", null), -1);
PropertyTypeDescriptor desc = new PropertyTypeDescriptor(prop, param);
FormattingConversionService service = (FormattingConversionService)this.applicationContext.getAutowireCapableBeanFactory().getBean("formattingFactory");
Assert.assertEquals("January", service.convert(data.getMonth(), desc, TypeDescriptor.valueOf(String.class)));
}
catch(Exception e) {
Assert.fail("Error formatting MonthName!\n" + e.getMessage());
}
}
@Test
public void FactoryCanConvertValueFromInt() {
try {
MonthNameData data = new MonthNameData(DateTimeConstants.JANUARY);
PropertyDescriptor prop = new PropertyDescriptor("month", MonthNameData.class);
MethodParameter param = MethodParameter.forMethodOrConstructor(MonthNameData.class.getMethod("setMonth", Integer.class), 0);
PropertyTypeDescriptor desc = new PropertyTypeDescriptor(prop, param);
FormattingConversionService service = (FormattingConversionService)this.applicationContext.getAutowireCapableBeanFactory().getBean("formattingFactory");
Assert.assertEquals(DateTimeConstants.JANUARY, service.convert("January", TypeDescriptor.valueOf(String.class), desc));
}
catch(Exception e) {
Assert.fail("Error formatting MonthName!\n" + e.getMessage());
}
}
@Test
public void GenericConversionServiceCanConvertValueToString() {
try {
MonthNameData data = new MonthNameData(DateTimeConstants.JANUARY);
PropertyDescriptor prop = new PropertyDescriptor("month", MonthNameData.class);
MethodParameter param = MethodParameter.forMethodOrConstructor(MonthNameData.class.getMethod("getMonth", null), -1);
PropertyTypeDescriptor desc = new PropertyTypeDescriptor(prop, param);
GenericConversionService service = this.applicationContext.getAutowireCapableBeanFactory().getBean(GenericConversionService.class);
Assert.assertEquals("January", service.convert(data.getMonth(), desc, TypeDescriptor.valueOf(String.class)));
}
catch(Exception e) {
Assert.fail("Error formatting MonthName!\n" + e.getMessage());
}
}
@Test
public void GenericConversionServiceCanConvertValueFromInt() {
try {
MonthNameData data = new MonthNameData(DateTimeConstants.JANUARY);
PropertyDescriptor prop = new PropertyDescriptor("month", MonthNameData.class);
MethodParameter param = MethodParameter.forMethodOrConstructor(MonthNameData.class.getMethod("setMonth", Integer.class), 0);
PropertyTypeDescriptor desc = new PropertyTypeDescriptor(prop, param);
GenericConversionService service = this.applicationContext.getAutowireCapableBeanFactory().getBean(GenericConversionService.class);
Assert.assertEquals(DateTimeConstants.JANUARY, service.convert("January", TypeDescriptor.valueOf(String.class), desc));
}
catch(Exception e) {
Assert.fail("Error formatting MonthName!\n" + e.getMessage());
}
}
}
It works with @NumberFormat
It works with @NumberFormat and I am using same SPI/API.
It works in 3.0.6 but not in 3.1.0 M2
The @NumberFormat will work with the spring:bind JSP tag under the 3.0.6 release. However, it doesn't work under the new 3.1.0 M2 release. Thanks for all the input on this issue.
Also, in another post it was mentioned that in 3.1 release you will be able to use SPEL expression in JSP page without binding or form tags and the formatting will be applied. Can someone on the Spring core team speak to this? Is it on the road map for RC of 3.1?
Thanks!