org.craftercms.commons.rest.HttpMessageConvertingResponseWriter Maven / Gradle / Ivy
/*
* Copyright (C) 2007-2014 Crafter Software Corporation.
*
* 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 .
*/
package org.craftercms.commons.rest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.craftercms.commons.i10n.I10nLogger;
import org.craftercms.commons.i10n.I10nUtils;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.context.request.ServletWebRequest;
/**
* Writes the response using a {@link org.springframework.http.converter.HttpMessageConverter} chosen depending on
* the acceptable media types from the request (most of the code is just a copy from Spring's {@code
* AbstractMessageConverterMethodProcessor}).
*
* @author avasquez
*/
public class HttpMessageConvertingResponseWriter {
private static final I10nLogger logger = new I10nLogger(HttpMessageConvertingResponseWriter.class,
I10nUtils.DEFAULT_LOGGING_MESSAGE_BUNDLE_NAME);
public static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application");
public static final String LOG_KEY_WRITTEN_WITH_MESSAGE_CONVERTER = "logging.rest.writtenWithMessageConverter";
protected ContentNegotiationManager contentNegotiationManager;
protected List> messageConverters;
protected List allSupportedMediaTypes;
public HttpMessageConvertingResponseWriter(ContentNegotiationManager contentNegotiationManager,
List> messageConverters) {
this.contentNegotiationManager = contentNegotiationManager;
this.messageConverters = messageConverters;
this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters);
}
@SuppressWarnings("unchecked")
public void writeWithMessageConverters(T returnValue, HttpServletRequest request, HttpServletResponse response)
throws IOException, HttpMediaTypeNotAcceptableException {
Class> returnValueClass = returnValue.getClass();
List requestedMediaTypes = getAcceptableMediaTypes(request);
List producibleMediaTypes = getProducibleMediaTypes(returnValueClass);
Set compatibleMediaTypes = new LinkedHashSet();
for (MediaType r : requestedMediaTypes) {
for (MediaType p : producibleMediaTypes) {
if (r.isCompatibleWith(p)) {
compatibleMediaTypes.add(getMostSpecificMediaType(r, p));
}
}
}
if (compatibleMediaTypes.isEmpty()) {
throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
}
List mediaTypes = new ArrayList(compatibleMediaTypes);
MediaType.sortBySpecificityAndQuality(mediaTypes);
MediaType selectedMediaType = null;
for (MediaType mediaType : mediaTypes) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
} else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
break;
}
}
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter> messageConverter : messageConverters) {
if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
((HttpMessageConverter) messageConverter).write(returnValue, selectedMediaType,
new ServletServerHttpResponse(response));
logger.debug(LOG_KEY_WRITTEN_WITH_MESSAGE_CONVERTER, returnValue, selectedMediaType,
messageConverter);
return;
}
}
}
throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
}
/**
* Returns the media types that can be produced:
*
* - Media types of configured converters that can write the specific return value, or
* - {@link org.springframework.http.MediaType#ALL}
*
*/
protected List getProducibleMediaTypes(Class> returnValueClass) {
if (!allSupportedMediaTypes.isEmpty()) {
List result = new ArrayList<>();
for (HttpMessageConverter> converter : messageConverters) {
if (converter.canWrite(returnValueClass, null)) {
result.addAll(converter.getSupportedMediaTypes());
}
}
return result;
}
else {
return Collections.singletonList(MediaType.ALL);
}
}
/**
* Return the acceptable media types from the request.
*/
protected List getAcceptableMediaTypes(HttpServletRequest request)
throws HttpMediaTypeNotAcceptableException {
List mediaTypes = contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request));
return mediaTypes.isEmpty() ? Collections.singletonList(MediaType.ALL) : mediaTypes;
}
/**
* Return the more specific of the acceptable and the producible media types with the q-value of the former.
*/
protected MediaType getMostSpecificMediaType(MediaType acceptType, MediaType produceType) {
produceType = produceType.copyQualityValue(acceptType);
return MediaType.SPECIFICITY_COMPARATOR.compare(acceptType, produceType) <= 0 ? acceptType : produceType;
}
/**
* Return the media types supported by all provided message converters sorted by specificity via
* {@link MediaType#sortBySpecificity(List)}.
*/
protected List getAllSupportedMediaTypes(List> messageConverters) {
Set allSupportedMediaTypes = new LinkedHashSet();
for (HttpMessageConverter> messageConverter : messageConverters) {
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
}
List result = new ArrayList(allSupportedMediaTypes);
MediaType.sortBySpecificity(result);
return Collections.unmodifiableList(result);
}
}