/*
* gvNIX is an open source tool for rapid application development (RAD).
* Copyright (C) 2010 Generalitat Valenciana
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gvnix.web.json;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.validation.Validator;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestDataBinderFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Overrides {@link RequestMappingHandlerAdapter} to create custom
* {@link ServletRequestDataBinderFactory}.
* <p/>
* {@link ServletRequestDataBinderFactory} will let us to recover the
* {@link DataBinder} used in deserialization process and place the
* {@link BindingResult} as Controller method parameter.
* <p/>
* It only handles request which {@link HandlerMethod}'s
* {@link RequestMapping#consumes()} is equals to "{@code application/json}"
* (and no more <em>consumes</em> types declared).
* <p/>
* To activate this adapter you must declare it on project's webmvc-config.xml.
* Example:
*
* <pre>
* {@code
*
* <!--
* Configures JSON content handling:
* - Registers custom Jackson2 MessageConverter
* - Registers custom Jackson2 ServletRequestDataBinderFactory to take
* binding errors in account
* -->
* <bean id="dataBinderRequestMappingHandlerAdapter" p:order="1"
* class="org.gvnix.web.json.Jackson2RequestMappingHandlerAdapter">
* <!-- Custom Jackson ObjectMapper delegates object
* serialization/deserialization to Spring ConversionService -->
* <property name="objectMapper">
* <bean class="org.gvnix.web.json.ConversionServiceObjectMapper" />
* </property>
* </bean>
*
* }
* </pre>
*
* @author <a href="http://www.disid.com">DISID Corporation S.L.</a> made for <a
* href="http://www.dgti.gva.es">General Directorate for Information
* Technologies (DGTI)</a>
* @since TODO: Class version
*/
public class Jackson2RequestMappingHandlerAdapter extends
RequestMappingHandlerAdapter {
private ObjectMapper objectMapper;
private final ConversionService conversionService;
private final Validator validator;
/**
* Default constructor.
*/
@Autowired
public Jackson2RequestMappingHandlerAdapter(
ConversionService conversionService, Validator validator) {
super();
this.conversionService = conversionService;
this.validator = validator;
}
/**
* Overrides the default implementation to create a
* {@link Jackson2ServletRequestDataBinderFactory} instance.
*
* @param binderMethods {@code @InitBinder} methods
* @return the Jackson2ServletRequestDataBinderFactory instance to use
* @throws Exception in case of invalid state or arguments
*/
protected ServletRequestDataBinderFactory createDataBinderFactory(
List<InvocableHandlerMethod> binderMethods) throws Exception {
return new Jackson2ServletRequestDataBinderFactory(binderMethods,
getWebBindingInitializer());
}
/**
* Setup custom {@link DataBinderMappingJackson2HttpMessageConverter}
*/
public void afterPropertiesSet() {
super.afterPropertiesSet();
DataBinderMappingJackson2HttpMessageConverter msgConverter = new DataBinderMappingJackson2HttpMessageConverter(
this.conversionService, this.validator);
msgConverter.setObjectMapper(this.objectMapper);
getMessageConverters().add(msgConverter);
}
public ObjectMapper getObjectMapper() {
return objectMapper;
}
public void setObjectMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
/**
* Check if handler method consumes json (and just json) messages to handle
* it
*
* @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#supportsInternal(org.springframework.web.method.HandlerMethod)
*/
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
// Get requestMapping annotation
RequestMapping requestMappingAnnotation = handlerMethod
.getMethodAnnotation(RequestMapping.class);
if (requestMappingAnnotation == null) {
// No annotation: don't handle
return false;
}
// Get consumes configuration
String[] consumes = requestMappingAnnotation.consumes();
if (consumes == null || consumes.length != 1) {
// No consumes configuration or multiple consumes: don't handle
return false;
}
// Check consume value
// TODO extract a constant
if (!"application/json".equals(consumes[0])) {
// Don't consumes json: don't handle
return false;
}
// Delegate on super for additional checks
return super.supportsInternal(handlerMethod);
}
}