3

I'm trying to upgrade a spring MVC app from 3.0.6 to 3.1.2 and some controllers that used to work don't seem to work anymore. I've read the spring docs, but I'm confused about what's compatible with what.

We've got a CustomWebArgumentResolver that looks for any request parameter named "asOf" and coverts its value to a date. We call it, unimaginatively, the "AsOfDateConverter." When upgrading to spring-3.1.2, I took advantage of the new namespace functionality and added this to my applicationContext:

<mvc:annotation-driven conversion-service="conversionService">
    <mvc:argument-resolvers>
        <bean id="customWebArgumentResolver" class="my.converters.CustomWebArgumentResolver">
        </bean>            
    </mvc:argument-resolvers>
</mvc:annotation-driven>

The CustomWebArgumentResolver is straightforward:

public class CustomWebArgumentResolver implements WebArgumentResolver {
    private AsOfDateConverter asOfDateConverter;

    @Override
    public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception {
        if (isAsOfDateParameter(methodParameter)) {
            return asOfDateConverter.convert(webRequest.getParameter("asOf"));
        }

        return UNRESOLVED;
    }

Then an example controller might look something like this:

@Controller
@Secured({BaseController.ROLE_LOGGED_IN})
@org.springframework.transaction.annotation.Transactional
public class DashboardController extends BaseController {
    public static final String URL = "/dashboard";

    @RequestMapping(value=URL, method=RequestMethod.GET)
    public ModelAndView get(@RequestParam(required=false) String requestedMeterType, @AsOf Date asOf) {
        debug(log, "Rendering dashboard asOf %s", asOf);
etc etc

The "asOf" parameter is coming in null, and I'm sure I'm missing something obvious. If anyone out there neck deep in the latest MVC 3.1 stuff could point me in the right direction I'd be grateful.

Thanks! Tom

EDIT: The AsOf annotation:

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface AsOf {
}

More of my applicationContext:

<mvc:annotation-driven conversion-service="conversionService">
    <mvc:argument-resolvers>
        <bean class="[blah].AsOfDateHandlerMethodArgumentResolver">
            <property name="asOfDateConverter">
                <bean class="[blah].AsOfDateConverter"/>
            </property>
        </bean> 
    </mvc:argument-resolvers>
</mvc:annotation-driven>

<!-- Added to re-support @Controller annotation scanning after upgrading to spring-3.1. -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>


<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="[blah].converters.CustomerConverter"/>
            <bean class="[blah].converters.AccountConverter"/>
            <bean class="[blah].converters.DateConverter"/>
            <bean class="[blah].converters.CustomerCommunicationInstanceConverter"/>
            <bean class="[blah].converters.MeterTypeConverter"/>
            <bean class="[blah].converters.AreaAmountConverter" p:precision="0"/>
            <bean class="[blah].converters.LengthAmountConverter" p:precision="1"/>
        </set>
    </property>
</bean>
user311121
  • 309
  • 1
  • 4
  • 14

1 Answers1

6

The API has changed with Spring 3.1 - the interface to implement to resolve a controller argument is HandlerMethodArgumentResolver. You can continue to use CustomWebArgumentResolver, by adapting it to a HandlerMethodArgumentResolver

However changing the code to use HandlerMethodArgumentResolver also will be easy:

public class CustomWebArgumentResolver implements HandlerMethodArgumentResolver {
    private AsOfDateConverter asOfDateConverter;

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
        if (isAsOfDateParameter(methodParameter)) {
            return asOfDateConverter.convert(webRequest.getParameter("asOf"));
        }

        return UNRESOLVED;

    }


    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return (methodParameter.getParameterAnnotation(AsOf.class)!=null)
    }

Edit

After looking through your comments, I think I have an idea about what could be going wrong. Can you please check your @AsOf annotation, you probably have not declared the retention of Runtime, which could be the reason why the the WebArgumentResolver is not taking effect:

@Retention(RetentionPolicy.RUNTIME)
public @interface AsOf {

}

Anyway here is a gist with a full working test along the same lines:

https://gist.github.com/3703430

Biju Kunjummen
  • 49,138
  • 14
  • 112
  • 125
  • Thanks for pointing out the API change. I've converted my CustomWebArgumentResolver from an implementation of the WebArgumentResolver to be an implementation (like yours) of the HandlerMethodArgumentResolver. After adding lots of logging, it seems like it is doing its job right, but the Controller is still getting set with a null Date (!). It's like the param is being mapped correctly to the Handler, but isn't being set back on the controller. So something about this still isn't working... – user311121 Sep 10 '12 at 22:23
  • Actually, on further thought the CustomWebArgumentResolver isn't getting passed any date strings at all... just nulls. Do I need ParamMappings on my Controller's get() method, just before the AsOf annotation? – user311121 Sep 10 '12 at 22:34
  • But you are grabbing the `asOf` from the request parameter right, so the issue you are having is that the asOf parameter is present and seems to come into the the resolveArgument method also, but in the method itself it is null is it – Biju Kunjummen Sep 11 '12 at 01:55
  • Logging statements are telling me that (1) the resolveArgument method is getting a null, (2) the resolveArgument method sees the null and decides to return a "new Date()", (3) the controller is injected with null. So somehow the input/output pipeline between the Controller and the HandlerMethodArgumentResolver is nulling out data. – user311121 Sep 11 '12 at 12:05
  • I have added a gist with a full test which works for me - https://gist.github.com/3703430, can you check. Can you also check if you have runtime retention on your annotation – Biju Kunjummen Sep 12 '12 at 01:10
  • The AsOf annotation is @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface AsOf { } – user311121 Sep 12 '12 at 12:50
  • I'm starting to wonder if the might be the issue. As shown in the original description, I've also got a conversionService defined in it, and some of the latest tests I ran make it seem like a TypeConverter is attempted to be run before the HandlerMethodArgumentResolver. I'll add additional snippets of my applicationContext.xml in case that reveals some clues. – user311121 Sep 12 '12 at 12:53
  • The `AnnotationMethodHandlerAdapter` is not required above right - any reason to have it, not likely to be the reason for the issue though. Is AsOfConverter not part of the list of converters in `ConversionServiceFactoryBean` – Biju Kunjummen Sep 12 '12 at 22:49