/* * Copyright 2016-2017, Youqian Yue ([email protected]). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.devefx.validator.http.reader.json; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Type; import java.nio.charset.Charset; import java.util.concurrent.atomic.AtomicReference; import javax.servlet.http.HttpServletRequest; import org.devefx.validator.http.MediaType; import org.devefx.validator.http.reader.AbstractHttpMessageReader; import org.devefx.validator.http.reader.HttpMessageNotReadableException; import org.devefx.validator.util.Assert; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; public abstract class AbstractJackson2HttpMessageReader extends AbstractHttpMessageReader<Object> { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); protected ObjectMapper objectMapper; private Boolean prettyPrint; protected AbstractJackson2HttpMessageReader(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } protected AbstractJackson2HttpMessageReader(ObjectMapper objectMapper, MediaType supportedMediaType) { super(supportedMediaType); this.objectMapper = objectMapper; } protected AbstractJackson2HttpMessageReader(ObjectMapper objectMapper, MediaType... supportedMediaTypes) { super(supportedMediaTypes); this.objectMapper = objectMapper; } /** * Set the {@code ObjectMapper} for this view. * If not set, a default {@link ObjectMapper#ObjectMapper() ObjectMapper} is used. * <p>Setting a custom-configured {@code ObjectMapper} is one way to take further * control of the JSON serialization process. For example, an extended * {@link com.fasterxml.jackson.databind.ser.SerializerFactory} * can be configured that provides custom serializers for specific types. * The other option for refining the serialization process is to use Jackson's * provided annotations on the types to be serialized, in which case a * custom-configured ObjectMapper is unnecessary. */ public void setObjectMapper(ObjectMapper objectMapper) { Assert.notNull(objectMapper, "ObjectMapper must not be null"); this.objectMapper = objectMapper; configurePrettyPrint(); } /** * Return the underlying {@code ObjectMapper} for this view. */ public ObjectMapper getObjectMapper() { return this.objectMapper; } /** * Whether to use the {@link DefaultPrettyPrinter} when writing JSON. * This is a shortcut for setting up an {@code ObjectMapper} as follows: * <pre class="code"> * ObjectMapper mapper = new ObjectMapper(); * mapper.configure(SerializationFeature.INDENT_OUTPUT, true); * converter.setObjectMapper(mapper); * </pre> */ public void setPrettyPrint(boolean prettyPrint) { this.prettyPrint = prettyPrint; configurePrettyPrint(); } private void configurePrettyPrint() { if (this.prettyPrint != null) { this.objectMapper.configure(SerializationFeature.INDENT_OUTPUT, this.prettyPrint); } } @Override protected boolean supports(Class<?> clazz) { JavaType javaType = getJavaType(clazz); AtomicReference<Throwable> causeRef = new AtomicReference<Throwable>(); if (this.objectMapper.canDeserialize(javaType, causeRef)) { return true; } Throwable cause = causeRef.get(); if (cause != null) { String msg = "Failed to evaluate deserialization for type " + javaType; if (logger.isDebugEnabled()) { logger.warn(msg, cause); } else { logger.warn(msg + ": " + cause); } } return false; } @Override protected Object readInternal(Class<? extends Object> clazz, HttpServletRequest request) throws IOException, HttpMessageNotReadableException { JavaType javaType = getJavaType(clazz); return readJavaType(javaType, request); } private Object readJavaType(JavaType javaType, HttpServletRequest request) { try { InputStream in = request.getInputStream(); return this.objectMapper.readValue(in, javaType); } catch (IOException ex) { throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex); } } protected JavaType getJavaType(Type type) { return this.objectMapper.getTypeFactory().constructType(type); } }