org.springframework.integration.http.inbound.HttpRequestHandlingController.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.integration.http.inbound.HttpRequestHandlingController.java

Source

/*
 * Copyright 2002-2019 the original author or authors.
 *
 * 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
 *
 *      https://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.springframework.integration.http.inbound;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.expression.Expression;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.http.RequestEntity;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.messaging.Message;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.validation.MapBindingResult;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.mvc.Controller;

/**
 * Inbound HTTP endpoint that implements Spring's {@link Controller} interface to be used with a DispatcherServlet front
 * controller.
 * <p>
 * The {@link #setViewName(String) viewName} will be passed into the ModelAndView return value.
 * <p>
 * This endpoint will have request/reply behavior by default. That can be overridden by passing <code>false</code> to
 * the constructor. In the request/reply case, the core map will be passed to the view, and it will contain either the
 * reply Message or payload depending on the value of {@link #setExtractReplyPayload(boolean)}
 * (true by default, meaning just the payload).
 * The corresponding key in the map is determined by the {@link #replyKey} property (with a default of "reply").
 *
 * @author Mark Fisher
 * @author Gary Russell
 * @author Artem Bilan
 *
 * @since 2.0
 */
public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSupport implements Controller {

    /**
     * The View model key for the error code.
     */
    public static final String DEFAULT_ERROR_CODE = "spring.integration.http.handler.error";

    /**
     * The View model key for reply.
     */
    public static final String DEFAULT_REPLY_KEY = "reply";

    /**
     * The View model key for errors.
     */
    public static final String DEFAULT_ERRORS_KEY = "errors";

    private Expression viewExpression;

    private StandardEvaluationContext evaluationContext;

    private String replyKey = DEFAULT_REPLY_KEY;

    private String errorsKey = DEFAULT_ERRORS_KEY;

    private String errorCode = DEFAULT_ERROR_CODE;

    public HttpRequestHandlingController() {
        this(true);
    }

    public HttpRequestHandlingController(boolean expectReply) {
        super(expectReply);
    }

    /**
     * Specify the view name.
     * @param viewName The view name.
     */
    public void setViewName(String viewName) {
        Assert.isTrue(StringUtils.hasText(viewName), "View name must contain text");
        this.viewExpression = new LiteralExpression(viewName);
    }

    /**
     * Specify the key to be used when adding the reply Message or payload to the core map (will be payload only unless
     * the value of {@link HttpRequestHandlingController#setExtractReplyPayload(boolean)} is <code>false</code>). The
     * default key is "reply".
     * @param replyKey The reply key.
     */
    public void setReplyKey(String replyKey) {
        this.replyKey = (replyKey != null) ? replyKey : DEFAULT_REPLY_KEY;
    }

    /**
     * The key used to expose {@link org.springframework.validation.Errors} in the core,
     * in the case that message handling fails. Defaults to "errors".
     * @param errorsKey The key value to set.
     */
    public void setErrorsKey(String errorsKey) {
        this.errorsKey = errorsKey;
    }

    /**
     * The error code to use to signal an error in the message handling. In the case of an error this code will be
     * provided in an object error to be optionally translated in the standard MVC way using a
     * {@link org.springframework.context.MessageSource}.
     * The default value is <code>spring.integration.http.handler.error</code>. Three arguments are provided: the
     * exception, its message and its stack trace as a String.
     * @param errorCode The error code to set.
     */
    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

    /**
     * Specifies a SpEL expression to evaluate in order to generate the view name.
     * The EvaluationContext will be populated with the reply message as the root object,
     * @param viewExpression The view expression.
     */
    public void setViewExpression(Expression viewExpression) {
        this.viewExpression = viewExpression;
    }

    @Override
    protected void onInit() {
        super.onInit();
        this.evaluationContext = this.createEvaluationContext();
    }

    /**
     * Handles the HTTP request by generating a Message and sending it to the request channel. If this gateway's
     * 'expectReply' property is true, it will also generate a response from the reply Message once received.
     */
    @Override
    public final ModelAndView handleRequest(HttpServletRequest servletRequest,
            HttpServletResponse servletResponse) {
        ModelAndView modelAndView = new ModelAndView();
        try {
            ServletServerHttpRequest request = prepareRequest(servletRequest);

            RequestEntity<Object> httpEntity = prepareRequestEntity(request);

            Message<?> replyMessage = doHandleRequest(servletRequest, httpEntity);
            ServletServerHttpResponse response = new ServletServerHttpResponse(servletResponse);
            if (replyMessage != null) {
                Object reply = setupResponseAndConvertReply(response, replyMessage);
                response.close();
                modelAndView.addObject(this.replyKey, reply);
            } else {
                setStatusCodeIfNeeded(response, httpEntity);
            }

            if (this.viewExpression != null) {
                Object view;
                if (replyMessage != null) {
                    view = this.viewExpression.getValue(this.evaluationContext, replyMessage);
                } else {
                    view = this.viewExpression.getValue(this.evaluationContext);
                }
                if (view instanceof View) {
                    modelAndView.setView((View) view);
                } else if (view instanceof String) {
                    modelAndView.setViewName((String) view);
                } else {
                    throw new IllegalStateException("view expression must resolve to a View or String");
                }
            }
        } catch (Exception e) {
            MapBindingResult errors = new MapBindingResult(new HashMap<String, Object>(), "dummy");
            PrintWriter stackTrace = new PrintWriter(new StringWriter());
            e.printStackTrace(stackTrace);
            errors.reject(this.errorCode, new Object[] { e, e.getMessage(), stackTrace.toString() },
                    "A Spring Integration handler raised an exception while handling an HTTP request.  "
                            + "The exception is of type " + e.getClass() + " and it has a message: ("
                            + e.getMessage() + ")");
            modelAndView.addObject(this.errorsKey, errors);
        }
        return modelAndView;
    }

}