org.alfresco.rest.framework.tools.ResponseWriter.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.rest.framework.tools.ResponseWriter.java

Source

/*-
 * #%L
 * Alfresco Remote API
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */

package org.alfresco.rest.framework.tools;

import org.alfresco.rest.framework.Api;
import org.alfresco.rest.framework.core.exceptions.DefaultExceptionResolver;
import org.alfresco.rest.framework.core.exceptions.ErrorResponse;
import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationException;
import org.alfresco.rest.framework.jacksonextensions.BeanPropertiesFilter;
import org.alfresco.rest.framework.jacksonextensions.ExecutionResult;
import org.alfresco.rest.framework.jacksonextensions.JacksonHelper;
import org.alfresco.rest.framework.resource.SerializablePagedCollection;
import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.content.ContentInfo;
import org.alfresco.rest.framework.resource.content.ContentInfoImpl;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.webscripts.WithResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.json.simple.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.extensions.surf.util.I18NUtil;
import org.springframework.extensions.webscripts.*;
import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/*
 * Writes to the response
 *
 * @author Gethin James
 */
public interface ResponseWriter {

    String UTF8 = "UTF-8";
    ContentInfo DEFAULT_JSON_CONTENT = new ContentInfoImpl(Format.JSON.mimetype(), UTF8, 0, null);
    Cache CACHE_NEVER = new Cache(new Description.RequiredCache() {
        @Override
        public boolean getNeverCache() {
            return true;
        }

        @Override
        public boolean getIsPublic() {
            return false;
        }

        @Override
        public boolean getMustRevalidate() {
            return true;
        }
    });

    final WithResponse DEFAULT_SUCCESS = new WithResponse(Status.STATUS_OK, DEFAULT_JSON_CONTENT, CACHE_NEVER);

    default Log resWriterLogger() {
        return LogFactory.getLog(this.getClass());
    }

    /**
     * Sets the response headers with any information we know about the content
     *
     * @param res         WebScriptResponse
     * @param contentInfo Content Information
     */
    default void setContentInfoOnResponse(WebScriptResponse res, ContentInfo contentInfo) {
        if (contentInfo != null) {
            //Set content info on the response
            res.setContentType(contentInfo.getMimeType());
            res.setContentEncoding(contentInfo.getEncoding());

            if (res instanceof WrappingWebScriptResponse) {
                WrappingWebScriptResponse wrappedRes = ((WrappingWebScriptResponse) res);
                res = wrappedRes.getNext();
            }

            if (res instanceof WebScriptServletResponse) {
                WebScriptServletResponse servletResponse = (WebScriptServletResponse) res;
                if (contentInfo.getLength() > 0) {
                    if (contentInfo.getLength() > 0 && contentInfo.getLength() < Integer.MAX_VALUE) {
                        servletResponse.getHttpServletResponse().setContentLength((int) contentInfo.getLength());
                    }
                }
                if (contentInfo.getLocale() != null) {
                    servletResponse.getHttpServletResponse().setLocale(contentInfo.getLocale());
                }
            }
        }
    }

    /**
     * The response status must be set before the response is written by Jackson (which will by default close and commit the response).
     * In a r/w txn, web script buffered responses ensure that it doesn't really matter but for r/o txns this is important.
     * If you set content information via the contentInfo object and ALSO the headers then "headers" will win because they are
     * set last.
     *
     * @param res
     * @param status
     * @param cache
     * @param contentInfo
     * @param headers
     */
    default void setResponse(final WebScriptResponse res, int status, Cache cache, ContentInfo contentInfo,
            Map<String, List<String>> headers) {
        res.setStatus(status);
        if (cache != null)
            res.setCache(cache);
        setContentInfoOnResponse(res, contentInfo);
        if (headers != null && !headers.isEmpty()) {
            for (Map.Entry<String, List<String>> header : headers.entrySet()) {
                for (int i = 0; i < header.getValue().size(); i++) {
                    if (i == 0) {
                        //If its the first one then set the header overwriting.
                        res.setHeader(header.getKey(), header.getValue().get(i));
                    } else {
                        //If its not the first one than update the header
                        res.addHeader(header.getKey(), header.getValue().get(i));
                    }
                }
            }
        }
    }

    /**
     * Sets the response using the WithResponse object
     *
     * @param res
     * @param withResponse
     */
    default void setResponse(final WebScriptResponse res, WithResponse withResponse) {
        setResponse(res, withResponse.getStatus(), withResponse.getCache(), withResponse.getContentInfo(),
                withResponse.getHeaders());
    }

    /**
     * Renders a JSON error response
     *
     * @param errorResponse The error
     * @param res           web script response
     * @throws IOException
     */
    default void renderErrorResponse(final ErrorResponse errorResponse, final WebScriptResponse res,
            final JacksonHelper jsonHelper) throws IOException {

        String logId = "";

        if (Status.STATUS_INTERNAL_SERVER_ERROR == errorResponse.getStatusCode()
                || resWriterLogger().isDebugEnabled()) {
            logId = org.alfresco.util.GUID.generate();
            resWriterLogger().error(logId + " : " + errorResponse.getStackTrace());
        }

        String stackMessage = I18NUtil.getMessage(DefaultExceptionResolver.STACK_MESSAGE_ID);

        final ErrorResponse errorToWrite = new ErrorResponse(errorResponse.getErrorKey(),
                errorResponse.getStatusCode(), errorResponse.getBriefSummary(), stackMessage, logId,
                errorResponse.getAdditionalState(), DefaultExceptionResolver.ERROR_URL);

        setContentInfoOnResponse(res, DEFAULT_JSON_CONTENT);

        // Status must be set before the response is written by Jackson (which will by default close and commit the response).
        // In a r/w txn, web script buffered responses ensure that it doesn't really matter but for r/o txns this is important.
        res.setStatus(errorToWrite.getStatusCode());

        jsonHelper.withWriter(res.getOutputStream(), new JacksonHelper.Writer() {
            @SuppressWarnings("unchecked")
            @Override
            public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)
                    throws JsonGenerationException, JsonMappingException, IOException {
                JSONObject obj = new JSONObject();
                obj.put("error", errorToWrite);
                objectMapper.writeValue(generator, obj);
            }
        });
    }

    /**
     * Renders an exception to the output stream as Json.
     *
     * @param exception
     * @param response
     * @throws IOException
     */
    default void renderException(final Exception exception, final WebScriptResponse response,
            final ApiAssistant assistant) throws IOException {
        renderErrorResponse(assistant.resolveException(exception), response, assistant.getJsonHelper());
    }

    /**
     * Renders the result of an execution.
     *
     * @param res         WebScriptResponse
     * @param toSerialize result of an execution
     * @throws IOException
     */
    default void renderJsonResponse(final WebScriptResponse res, final Object toSerialize,
            final JacksonHelper jsonHelper) throws IOException {
        jsonHelper.withWriter(res.getOutputStream(), new JacksonHelper.Writer() {
            @Override
            public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)
                    throws JsonGenerationException, JsonMappingException, IOException {
                objectMapper.writeValue(generator, toSerialize);
            }
        });
    }

}