org.craftercms.commons.rest.HttpMessageConvertingResponseWriter.java Source code

Java tutorial

Introduction

Here is the source code for org.craftercms.commons.rest.HttpMessageConvertingResponseWriter.java

Source

/*
 * 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 <http://www.gnu.org/licenses/>.
 */
package org.craftercms.commons.rest;

import org.craftercms.commons.i10n.I10nLogger;
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;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * 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,
            "crafter.commons.messages.logging");

    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<HttpMessageConverter<?>> messageConverters;
    protected List<MediaType> allSupportedMediaTypes;

    public HttpMessageConvertingResponseWriter(ContentNegotiationManager contentNegotiationManager,
            List<HttpMessageConverter<?>> messageConverters) {
        this.contentNegotiationManager = contentNegotiationManager;
        this.messageConverters = messageConverters;
        this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters);
    }

    public <T> void writeWithMessageConverters(T returnValue, HttpServletRequest request,
            HttpServletResponse response) throws IOException, HttpMediaTypeNotAcceptableException {
        Class<?> returnValueClass = returnValue.getClass();
        List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
        List<MediaType> producibleMediaTypes = getProducibleMediaTypes(returnValueClass);

        Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
        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<MediaType> mediaTypes = new ArrayList<MediaType>(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<T>) 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:
     * <ul>
     *    <li>Media types of configured converters that can write the specific return value, or</li>
     *    <li>{@link org.springframework.http.MediaType#ALL}</li>
     * </ul>
     */
    protected List<MediaType> getProducibleMediaTypes(Class<?> returnValueClass) {
        if (!allSupportedMediaTypes.isEmpty()) {
            List<MediaType> 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<MediaType> getAcceptableMediaTypes(HttpServletRequest request)
            throws HttpMediaTypeNotAcceptableException {
        List<MediaType> 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<MediaType> getAllSupportedMediaTypes(List<HttpMessageConverter<?>> messageConverters) {
        Set<MediaType> allSupportedMediaTypes = new LinkedHashSet<MediaType>();
        for (HttpMessageConverter<?> messageConverter : messageConverters) {
            allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
        }

        List<MediaType> result = new ArrayList<MediaType>(allSupportedMediaTypes);

        MediaType.sortBySpecificity(result);

        return Collections.unmodifiableList(result);
    }

}