diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java index 0ad7faf5ff..aaf11db599 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/config/HttpInboundEndpointParser.java @@ -176,6 +176,12 @@ public class HttpInboundEndpointParser extends AbstractSingleBeanDefinitionParse builder.addPropertyValue("requestMapping", requestMappingDef); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "request-payload-type", "requestPayloadType"); + + BeanDefinition statusCodeExpressionDef = + IntegrationNamespaceUtils.createExpressionDefIfAttributeDefined("status-code-expression", element); + if (statusCodeExpressionDef != null) { + builder.addPropertyValue("statusCodeExpression", statusCodeExpressionDef); + } } private String getInputChannelAttributeName() { diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingController.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingController.java index 059ca4778d..e8234bf342 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingController.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingController.java @@ -51,6 +51,7 @@ import org.springframework.web.servlet.mvc.Controller; * * @author Mark Fisher * @author Gary Russell + * @author Artem Bilan * @since 2.0 */ public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSupport implements Controller { @@ -148,12 +149,16 @@ public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSu ModelAndView modelAndView = new ModelAndView(); try { Message replyMessage = super.doHandleRequest(servletRequest, servletResponse); + ServletServerHttpResponse response = new ServletServerHttpResponse(servletResponse); if (replyMessage != null) { - ServletServerHttpResponse response = new ServletServerHttpResponse(servletResponse); Object reply = setupResponseAndConvertReply(response, replyMessage); response.close(); modelAndView.addObject(this.replyKey, reply); } + else { + setStatusCodeIfNeeded(response); + } + if (this.viewExpression != null) { Object view; if (replyMessage != null) { @@ -184,4 +189,5 @@ public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSu } return modelAndView; } + } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java index 86971a85cf..0193943e9e 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java @@ -31,6 +31,7 @@ import javax.xml.transform.Source; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.http.HttpEntity; @@ -151,6 +152,10 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa private volatile boolean shuttingDown; + private volatile Expression statusCodeExpression; + + private volatile EvaluationContext evaluationContext; + private final AtomicInteger activeCount = new AtomicInteger(); public HttpRequestHandlingEndpointSupport() { @@ -315,6 +320,19 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa this.multipartResolver = multipartResolver; } + /** + * Specify the {@link Expression} to resolve a status code for Response + * to override the default '200 OK'. + *

The {@link #statusCodeExpression} is applied only for the one-way {@code }. + * The {@code } resolves an {@link HttpStatus} from the + * {@link org.springframework.integration.http.HttpHeaders#STATUS_CODE} reply {@link Message} header. + * @param statusCodeExpression The status code Expression. + * @since 4.1 + */ + public void setStatusCodeExpression(Expression statusCodeExpression) { + this.statusCodeExpression = statusCodeExpression; + } + @Override public String getComponentType() { return (this.expectReply) ? "http:inbound-gateway" : "http:inbound-channel-adapter"; @@ -351,6 +369,15 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa this.messageConverters.addAll(this.defaultMessageConverters); } this.validateSupportedMethods(); + + if (this.expectReply && this.statusCodeExpression != null) { + logger.warn("The 'statusCodeExpression' is ignored when " + + "this component is configured as request/reply gateway"); + } + + if (this.statusCodeExpression != null) { + this.evaluationContext = createEvaluationContext(); + } } /** @@ -515,6 +542,19 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa } + protected void setStatusCodeIfNeeded(ServletServerHttpResponse response) { + if (this.statusCodeExpression != null) { + if (this.evaluationContext == null) { + this.evaluationContext = createEvaluationContext(); + } + Object value = this.statusCodeExpression.getValue(this.evaluationContext); + HttpStatus httpStatus = buildHttpStatus(value); + if (httpStatus != null) { + response.setStatusCode(httpStatus); + } + } + } + /** * Prepares an instance of {@link ServletServerHttpRequest} from the raw * {@link HttpServletRequest}. Also converts the request into a multipart request to @@ -586,15 +626,19 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa private HttpStatus resolveHttpStatusFromHeaders(MessageHeaders headers) { Object httpStatusFromHeader = headers.get(org.springframework.integration.http.HttpHeaders.STATUS_CODE); + return buildHttpStatus(httpStatusFromHeader); + } + + private HttpStatus buildHttpStatus(Object httpStatusValue) { HttpStatus httpStatus = null; - if (httpStatusFromHeader instanceof HttpStatus) { - httpStatus = (HttpStatus) httpStatusFromHeader; + if (httpStatusValue instanceof HttpStatus) { + httpStatus = (HttpStatus) httpStatusValue; } - else if (httpStatusFromHeader instanceof Integer) { - httpStatus = HttpStatus.valueOf((Integer) httpStatusFromHeader); + else if (httpStatusValue instanceof Integer) { + httpStatus = HttpStatus.valueOf((Integer) httpStatusValue); } - else if (httpStatusFromHeader instanceof String) { - httpStatus = HttpStatus.valueOf(Integer.parseInt((String) httpStatusFromHeader)); + else if (httpStatusValue instanceof String) { + httpStatus = HttpStatus.valueOf(Integer.parseInt((String) httpStatusValue)); } return httpStatus; } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGateway.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGateway.java index 4ddbafdd52..5feaa0a6ce 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGateway.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGateway.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -57,9 +57,11 @@ import org.springframework.web.HttpRequestHandler; * * @author Mark Fisher * @author Oleg Zhurakousky + * @author Artem Bilan * @since 2.0 */ -public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndpointSupport implements HttpRequestHandler { +public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndpointSupport + implements HttpRequestHandler { private volatile boolean convertExceptions; @@ -115,6 +117,9 @@ public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndp this.writeResponse(responseContent, response, request.getHeaders().getAccept()); } } + else { + setStatusCodeIfNeeded(response); + } } private Object handleExceptionInternal(Exception e) throws IOException { @@ -135,7 +140,8 @@ public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndp } @SuppressWarnings({"unchecked", "rawtypes"}) - private void writeResponse(Object content, ServletServerHttpResponse response, List acceptTypes) throws IOException { + private void writeResponse(Object content, ServletServerHttpResponse response, List acceptTypes) + throws IOException { if (CollectionUtils.isEmpty(acceptTypes)) { acceptTypes = Collections.singletonList(MediaType.ALL); } diff --git a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-4.1.xsd b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-4.1.xsd index 4041a7c196..7d4b1aa857 100644 --- a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-4.1.xsd +++ b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-4.1.xsd @@ -52,6 +52,23 @@ + + + + A SpEL expression that resolves to an 'HttpStatus' code when rendering a response. + The expression must return the object which can be converted to a + 'org.springframework.http.HttpStatus' enum value. + The 'evaluationContext' has a 'BeanResolver' but no variables, so the usage of this attribute + is somewhat limited. + An example might be to resolve, at runtime, some scoped Bean that returns an + 'HttpStatus' value. + By default 'status-code-expression' is null, meaning that the default '200 OK' response status + will be returned. + The 'http:inbound-gateway' resolves the 'status code' from the 'http_statusCode' header of the reply + Message. + + + @@ -211,7 +228,7 @@ The expression can resolve to a view name or View object. In the case of 'inbound-gateway' the root object of the evaluation context is the reply message. In the case of 'inbound-channel-adapter' the 'evaluationContext' for this expression - is rather lightweight, because there is no reply message, ; it has a + is rather lightweight, because there is no reply message, it has a 'BeanResolver' but no variables, so the usage of this attribute is somewhat limited. An example might be to resolve, at runtime, some scoped Bean that returns a diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests-context.xml b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests-context.xml index 03487727c8..80b00e77c0 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests-context.xml +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests-context.xml @@ -20,7 +20,8 @@ - + @@ -38,7 +39,8 @@ - + diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java index 5745e06bed..18c1b87aa1 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/HttpInboundChannelAdapterParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -128,7 +128,7 @@ public class HttpInboundChannelAdapterParserTests extends AbstractHttpInboundTes request.setParameter("foo", "bar"); MockHttpServletResponse response = new MockHttpServletResponse(); defaultAdapter.handleRequest(request, response); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + assertEquals(HttpServletResponse.SC_SWITCHING_PROTOCOLS, response.getStatus()); Message message = requests.receive(0); assertNotNull(message); Object payload = message.getPayload(); @@ -256,6 +256,15 @@ public class HttpInboundChannelAdapterParserTests extends AbstractHttpInboundTes assertEquals("oops", errorCode); Expression viewExpression = TestUtils.getPropertyValue(inboundController, "viewExpression", Expression.class); assertEquals("foo", viewExpression.getExpressionString()); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("GET"); + request.setParameter("foo", "bar"); + MockHttpServletResponse response = new MockHttpServletResponse(); + inboundController.handleRequest(request, response); + assertEquals(HttpServletResponse.SC_ACCEPTED, response.getStatus()); + Message message = requests.receive(0); + assertNotNull(message); } @Test diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java index 7ef211cbdb..34f007a769 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -31,6 +31,7 @@ import java.util.List; import org.junit.Test; import org.springframework.beans.factory.BeanFactory; +import org.springframework.expression.common.LiteralExpression; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; @@ -116,6 +117,7 @@ public class HttpRequestHandlingMessagingGatewayTests extends AbstractHttpInboun } }); HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true); + gateway.setStatusCodeExpression(new LiteralExpression("foo")); gateway.setBeanFactory(mock(BeanFactory.class)); gateway.setRequestPayloadType(String.class); gateway.setRequestChannel(requestChannel); diff --git a/src/reference/docbook/http.xml b/src/reference/docbook/http.xml index e1d3e8b2fc..842eb92330 100644 --- a/src/reference/docbook/http.xml +++ b/src/reference/docbook/http.xml @@ -317,6 +317,29 @@ By default the HTTP request will be generated using an instance of Si + Response StatusCode + + Starting with version 4.1 the <http:inbound-channel-adapter> + can be configured with a status-code-expression to override the default 200 OK status. + The expression must return an object which can be converted to a + org.springframework.http.HttpStatus enum value. + The evaluationContext has a BeanResolver but no variables, + so the usage of this attribute is somewhat limited. + An example might be to resolve, at runtime, some scoped Bean that returns a status code value but, + most likely, it will be set to a fixed value + such as status-code=expression="'204'" (No Content), or + status-code-expression="T(org.springframework.http.HttpStatus).NO_CONTENT". + By default, status-code-expression is null meaning that the normal '200 OK' response status + will be returned. + + +]]> + The <http:inbound-gateway> resolves the 'status code' from the http_statusCode + header of the reply Message. + + URI Template Variables and Expressions By Using the path attribute in conjunction with the diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml index c16602b624..9dace90410 100644 --- a/src/reference/docbook/whats-new.xml +++ b/src/reference/docbook/whats-new.xml @@ -46,5 +46,13 @@ before sending the request. +

+ Http Inbound Channel Adapter and StatusCode + + The <http:inbound-channel-adapter> can now be configured with a + status-code-expression to override the default 200 OK status. + See for more information. + +