From 37f25dff4019022418e38223720f14ffc4bc1987 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Tue, 30 Aug 2011 12:23:39 -0400 Subject: [PATCH 01/12] INT-1677 added initial support for mapping inbound variables to Http Inbound Gateway --- .../HttpRequestHandlingEndpointSupport.java | 31 ++++++- ...gMessagingGatewayWithPathMappingTests.java | 80 +++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java 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 363b016060..5ccc417177 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 @@ -19,6 +19,7 @@ package org.springframework.integration.http.inbound; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -54,9 +55,11 @@ import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.MultipartResolver; import org.springframework.web.servlet.DispatcherServlet; +import org.springframework.web.util.UriTemplate; /** * Base class for HTTP request handling endpoints. @@ -104,6 +107,8 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor private final boolean expectReply; + private volatile String path; + private volatile boolean extractReplyPayload = true; private volatile MultipartResolver multipartResolver; @@ -140,6 +145,10 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor protected boolean isExpectReply() { return expectReply; } + + public void setPath(String path) { + this.path = path; + } /** * Set the message body converters to use. These converters are used to convert from and to HTTP requests and @@ -244,6 +253,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor * 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. */ + @SuppressWarnings({ "rawtypes", "unchecked" }) protected final Object doHandleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException { try { @@ -252,14 +262,33 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor servletResponse.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); return null; } + Map uriVariableMappings = null; + // + if (StringUtils.hasText(this.path)){ + UriTemplate template = new UriTemplate(this.path); + uriVariableMappings = template.match(request.getURI().getPath()); + if (logger.isDebugEnabled()){ + logger.debug("Mapped URI variables: " + uriVariableMappings); + } + } + // + + Map headers = this.headerMapper.toHeaders(request.getHeaders()); + Object payload = null; if (this.isReadable(request)) { payload = this.generatePayloadFromRequestBody(request); + headers.putAll(uriVariableMappings); } else { payload = this.convertParameterMap(servletRequest.getParameterMap()); + if (payload instanceof Map){ + for (Object key : uriVariableMappings.keySet()) { + ((Map) payload).put(key, Collections.singletonList(uriVariableMappings.get(key))); + } + } } - Map headers = this.headerMapper.toHeaders(request.getHeaders()); + Message message = MessageBuilder.withPayload(payload).copyHeaders(headers).setHeader( org.springframework.integration.http.HttpHeaders.REQUEST_URL, request.getURI().toString()) .setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_METHOD, diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java new file mode 100644 index 0000000000..c52ca74e74 --- /dev/null +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2002-2010 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 + * + * http://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.util.Collections; +import java.util.Map; + +import org.junit.Test; +import org.springframework.integration.Message; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.http.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +/** + * @author Oleg Zhurakousky + */ +public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { + + @Test + public void defaultUriVariableMappingWithPOST() throws Exception { + QueueChannel requestChannel = new QueueChannel(); + HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(false); + gateway.setPath("/fname/{f}/lname/{l}"); + gateway.setRequestChannel(requestChannel); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("POST"); + request.setContentType("text/plain"); + request.setParameter("foo", "bar"); + request.setContent("hello".getBytes()); + request.setRequestURI("/fname/bill/lname/clinton"); + MockHttpServletResponse response = new MockHttpServletResponse(); + gateway.handleRequest(request, response); + Message message = requestChannel.receive(0); + assertNotNull(message); + assertEquals("bill", message.getHeaders().get("f")); + assertEquals("clinton", message.getHeaders().get("l")); + } + + @SuppressWarnings("rawtypes") + @Test + public void defaultUriVariableMappingWithGET() throws Exception { + QueueChannel requestChannel = new QueueChannel(); + HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(false); + gateway.setPath("/fname/{f}/lname/{l}"); + gateway.setRequestChannel(requestChannel); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("GET"); + request.setParameter("foo", "bar"); + request.setRequestURI("/fname/bill/lname/clinton"); + MockHttpServletResponse response = new MockHttpServletResponse(); + gateway.handleRequest(request, response); + Message message = requestChannel.receive(0); + assertNotNull(message); + assertNull(message.getHeaders().get("f")); + assertNull(message.getHeaders().get("l")); + Map payload = (Map) message.getPayload(); + assertEquals(Collections.singletonList("bill"), payload.get("f")); + assertEquals(Collections.singletonList("clinton"), payload.get("l")); + } + + +} From bff8c9a9e6328ba12b6efc4ed9198f088399628e Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Tue, 30 Aug 2011 18:30:12 -0400 Subject: [PATCH 02/12] INT-1677 added initial spell support for extracting Request data --- .../HttpRequestHandlingEndpointSupport.java | 63 ++++++++++++++++- ...gMessagingGatewayWithPathMappingTests.java | 69 ++++++++++++++++++- 2 files changed, 128 insertions(+), 4 deletions(-) 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 5ccc417177..88e13b5bf0 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 @@ -17,6 +17,7 @@ package org.springframework.integration.http.inbound; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -28,6 +29,14 @@ import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.context.expression.MapAccessor; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.converter.Converter; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.expression.spel.support.StandardTypeConverter; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -82,9 +91,12 @@ import org.springframework.web.util.UriTemplate; * false. * * @author Mark Fisher + * @author Oleg Zhurakousky * @since 2.0 */ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySupport { + + private static final ExpressionParser PARSER = new SpelExpressionParser(); private static final boolean jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", HttpRequestHandlingEndpointSupport.class.getClassLoader()); @@ -112,6 +124,8 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor private volatile boolean extractReplyPayload = true; private volatile MultipartResolver multipartResolver; + + private final StandardEvaluationContext evaluationContext; public HttpRequestHandlingEndpointSupport() { this(true); @@ -137,6 +151,9 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor // this.messageConverters.add(new AtomFeedHttpMessageConverter()); // this.messageConverters.add(new RssChannelHttpMessageConverter()); } + StandardEvaluationContext sec = new StandardEvaluationContext(); + sec.addPropertyAccessor(new MapAccessor()); + this.evaluationContext = sec; } /** @@ -247,6 +264,13 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor } } } + if (beanFactory != null) { + this.evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory)); + } + ConversionService conversionService = this.getConversionService(); + if (conversionService != null) { + this.evaluationContext.setTypeConverter(new StandardTypeConverter(conversionService)); + } } /** @@ -277,7 +301,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor Object payload = null; if (this.isReadable(request)) { - payload = this.generatePayloadFromRequestBody(request); + payload = this.extractRequestBody(request); headers.putAll(uriVariableMappings); } else { @@ -336,7 +360,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor if (this.multipartResolver != null && this.multipartResolver.isMultipart(servletRequest)) { return new MultipartHttpInputMessage(this.multipartResolver.resolveMultipart(servletRequest)); } - return new ServletServerHttpRequest(servletRequest); + return new SmartServletServerHttpRequest(servletRequest); } /** @@ -377,7 +401,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor } @SuppressWarnings({"unchecked", "rawtypes"}) - private Object generatePayloadFromRequestBody(ServletServerHttpRequest request) throws IOException { + private Object extractRequestBody(ServletServerHttpRequest request) throws IOException { MediaType contentType = request.getHeaders().getContentType(); Class expectedType = this.requestPayloadType; if (expectedType == null) { @@ -407,5 +431,38 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor } return httpStatus; } + + public class SmartServletServerHttpRequest extends ServletServerHttpRequest{ + + private final Map requestParamMap; + @SuppressWarnings("rawtypes") + private final Map uriVarsMap; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public SmartServletServerHttpRequest(HttpServletRequest servletRequest) { + super(servletRequest); + + requestParamMap = servletRequest.getParameterMap(); + Map uriVariableMappings = null; + // + if (StringUtils.hasText(HttpRequestHandlingEndpointSupport.this.path)){ + UriTemplate template = new UriTemplate(HttpRequestHandlingEndpointSupport.this.path); + uriVariableMappings = template.match(this.getURI().getPath()); + if (logger.isDebugEnabled()){ + logger.debug("Mapped URI variables: " + uriVariableMappings); + } + } + uriVarsMap = uriVariableMappings; + } + + public Map getRequestParamMap() { + return requestParamMap; + } + + @SuppressWarnings("rawtypes") + public Map getUriVars() { + return uriVarsMap; + } + } } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java index c52ca74e74..718614a4b3 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java @@ -16,18 +16,32 @@ package org.springframework.integration.http.inbound; +import java.io.IOException; +import java.io.InputStream; import java.util.Collections; +import java.util.List; import java.util.Map; import org.junit.Test; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.support.ConversionServiceFactory; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.expression.spel.support.StandardTypeConverter; +import org.springframework.http.HttpHeaders; import org.springframework.integration.Message; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.http.MockHttpServletRequest; +import org.springframework.integration.http.inbound.HttpRequestHandlingEndpointSupport.SmartServletServerHttpRequest; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.util.CollectionUtils; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; /** * @author Oleg Zhurakousky @@ -75,6 +89,59 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { assertEquals(Collections.singletonList("bill"), payload.get("f")); assertEquals(Collections.singletonList("clinton"), payload.get("l")); } + /** + * This is a temporary test which simply shows what would happen inside of the gateway which has an internal + * capability for SpEL based extraction of data such as UriVariabe mappings, HttpHeaders, Body and Request Parameters. + * + * @throws Exception + */ + @Test + public void withExpression() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("POST"); + request.setContentType("text/plain"); + request.setParameter("foo", "bar"); + request.setContent("hello".getBytes()); + request.setRequestURI("/fname/bill/lname/clinton"); + + HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true); + gateway.setPath("/fname/{f}/lname/{l}"); + + SmartServletServerHttpRequest smartRequest = gateway.new SmartServletServerHttpRequest(request); + + StandardEvaluationContext context = new StandardEvaluationContext(); + ConversionService conversionService = ConversionServiceFactory.createDefaultConversionService(); + + context.setTypeConverter(new StandardTypeConverter(conversionService)); + + ExpressionParser parser = new SpelExpressionParser(); + + Expression bodyExpression = parser.parseExpression("#this.body"); + InputStream body = bodyExpression.getValue(context, smartRequest, InputStream.class); + assertNotNull(body); + assertEquals("hello", this.convertToString(body)); + + Expression headersExpression = parser.parseExpression("#this.headers['Content-Type']"); + List headers = headersExpression.getValue(context, smartRequest, List.class); + assertNotNull(headers); + assertEquals("text/plain", headers.get(0)); + + Expression uriVarsExpression = parser.parseExpression("#this.uriVars"); + Map uriVars = uriVarsExpression.getValue(context, smartRequest, Map.class); + assertNotNull(uriVars); + assertTrue(!CollectionUtils.isEmpty(uriVars)); + assertEquals("bill", "f"); + assertEquals("bill", "f"); + + } + + public String convertToString(InputStream in) throws IOException { + StringBuffer out = new StringBuffer(); + byte[] b = new byte[4096]; + for (int n; (n = in.read(b)) != -1;) { + out.append(new String(b, 0, n)); + } + return out.toString(); + } - } From ff423b1336c8ec4e813129da3ce4074ca3e8983b Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Tue, 30 Aug 2011 21:46:29 -0400 Subject: [PATCH 03/12] INT-1677 polished test --- ...dlingMessagingGatewayWithPathMappingTests.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java index 718614a4b3..27ffcdd7a5 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java @@ -116,23 +116,20 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { ExpressionParser parser = new SpelExpressionParser(); - Expression bodyExpression = parser.parseExpression("#this.body"); + Expression bodyExpression = parser.parseExpression("body"); InputStream body = bodyExpression.getValue(context, smartRequest, InputStream.class); assertNotNull(body); assertEquals("hello", this.convertToString(body)); - Expression headersExpression = parser.parseExpression("#this.headers['Content-Type']"); + Expression headersExpression = parser.parseExpression("headers['Content-Type']"); List headers = headersExpression.getValue(context, smartRequest, List.class); assertNotNull(headers); assertEquals("text/plain", headers.get(0)); - Expression uriVarsExpression = parser.parseExpression("#this.uriVars"); - Map uriVars = uriVarsExpression.getValue(context, smartRequest, Map.class); - assertNotNull(uriVars); - assertTrue(!CollectionUtils.isEmpty(uriVars)); - assertEquals("bill", "f"); - assertEquals("bill", "f"); - + Expression uriVarsExpression = parser.parseExpression("uriVars['f']"); + String fname = uriVarsExpression.getValue(context, smartRequest, String.class); + assertNotNull(fname); + assertEquals("bill", fname); } public String convertToString(InputStream in) throws IOException { From 897bc887a1a5e6003081e2f58dabf7ed031ec0d2 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Wed, 31 Aug 2011 14:41:59 -0400 Subject: [PATCH 04/12] INT-1677 work in progress --- .../inbound/HttpRequestHandlingEndpointSupport.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) 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 88e13b5bf0..4eecd4f4f6 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 @@ -33,6 +33,7 @@ import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.context.expression.MapAccessor; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.converter.Converter; +import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; @@ -126,6 +127,10 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor private volatile MultipartResolver multipartResolver; private final StandardEvaluationContext evaluationContext; + + private volatile Expression payloadExpression; + + private volatile List headerExpressions; public HttpRequestHandlingEndpointSupport() { this(true); @@ -167,6 +172,13 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor this.path = path; } + public void setPayloadExpression(Expression payloadExpression) { + this.payloadExpression = payloadExpression; + } + + public void setHeaderExpressions(List headerExpressions) { + this.headerExpressions = headerExpressions; + } /** * Set the message body converters to use. These converters are used to convert from and to HTTP requests and * responses. @@ -441,6 +453,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor @SuppressWarnings({ "rawtypes", "unchecked" }) public SmartServletServerHttpRequest(HttpServletRequest servletRequest) { + //servletRequest.getp super(servletRequest); requestParamMap = servletRequest.getParameterMap(); From 224db99c155c12fb12ae7977763b3956e97b4dc8 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Thu, 1 Sep 2011 14:02:38 -0400 Subject: [PATCH 05/12] INT-1677 added support for expressions --- .../HttpRequestHandlingEndpointSupport.java | 95 +++++----- ...gMessagingGatewayWithPathMappingTests.java | 173 +++++++++--------- 2 files changed, 124 insertions(+), 144 deletions(-) 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 4eecd4f4f6..083dfbb3b2 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 @@ -21,6 +21,7 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -38,6 +39,7 @@ import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardTypeConverter; +import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -97,8 +99,6 @@ import org.springframework.web.util.UriTemplate; */ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySupport { - private static final ExpressionParser PARSER = new SpelExpressionParser(); - private static final boolean jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", HttpRequestHandlingEndpointSupport.class.getClassLoader()); @@ -130,7 +130,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor private volatile Expression payloadExpression; - private volatile List headerExpressions; + private volatile Map headerExpressions; public HttpRequestHandlingEndpointSupport() { this(true); @@ -176,7 +176,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor this.payloadExpression = payloadExpression; } - public void setHeaderExpressions(List headerExpressions) { + public void setHeaderExpressions(Map headerExpressions) { this.headerExpressions = headerExpressions; } /** @@ -303,27 +303,51 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor if (StringUtils.hasText(this.path)){ UriTemplate template = new UriTemplate(this.path); uriVariableMappings = template.match(request.getURI().getPath()); - if (logger.isDebugEnabled()){ - logger.debug("Mapped URI variables: " + uriVariableMappings); + if (!uriVariableMappings.isEmpty()){ + if (logger.isDebugEnabled()){ + logger.debug("Mapped URI variables: " + uriVariableMappings); + } + + // set the whole map + this.evaluationContext.setVariable("uriVariables", uriVariableMappings); + for (Object key : uriVariableMappings.keySet()) { + // add individual elements + this.evaluationContext.setVariable((String) key, uriVariableMappings.get(key)); + } } + else { + logger.warn("Was not able to match UriVariables to path: " + this.path); + } } // Map headers = this.headerMapper.toHeaders(request.getHeaders()); - Object payload = null; - if (this.isReadable(request)) { - payload = this.extractRequestBody(request); - headers.putAll(uriVariableMappings); - } - else { - payload = this.convertParameterMap(servletRequest.getParameterMap()); - if (payload instanceof Map){ - for (Object key : uriVariableMappings.keySet()) { - ((Map) payload).put(key, Collections.singletonList(uriVariableMappings.get(key))); - } + Object payload = null; + + if (this.payloadExpression != null){ + // create payload based on SpEL + payload = this.payloadExpression.getValue(this.evaluationContext, request); + } + if (this.headerExpressions != null){ + for (String headerName : this.headerExpressions.keySet()) { + Expression headerExpression = this.headerExpressions.get(headerName); + Object headerValue = headerExpression.getValue(this.evaluationContext, request); + ((Map)headers).put(headerName, headerValue); + } + } + + if (payload == null){ + if (this.isReadable(request)) { + payload = this.extractRequestBody(request); + } + else { + payload = this.convertParameterMap(servletRequest.getParameterMap()); + } } + + HttpEntity entity = new HttpEntity(request.getBody(), request.getHeaders()); Message message = MessageBuilder.withPayload(payload).copyHeaders(headers).setHeader( org.springframework.integration.http.HttpHeaders.REQUEST_URL, request.getURI().toString()) @@ -372,7 +396,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor if (this.multipartResolver != null && this.multipartResolver.isMultipart(servletRequest)) { return new MultipartHttpInputMessage(this.multipartResolver.resolveMultipart(servletRequest)); } - return new SmartServletServerHttpRequest(servletRequest); + return new ServletServerHttpRequest(servletRequest); } /** @@ -443,39 +467,4 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor } return httpStatus; } - - public class SmartServletServerHttpRequest extends ServletServerHttpRequest{ - - private final Map requestParamMap; - - @SuppressWarnings("rawtypes") - private final Map uriVarsMap; - - @SuppressWarnings({ "rawtypes", "unchecked" }) - public SmartServletServerHttpRequest(HttpServletRequest servletRequest) { - //servletRequest.getp - super(servletRequest); - - requestParamMap = servletRequest.getParameterMap(); - Map uriVariableMappings = null; - // - if (StringUtils.hasText(HttpRequestHandlingEndpointSupport.this.path)){ - UriTemplate template = new UriTemplate(HttpRequestHandlingEndpointSupport.this.path); - uriVariableMappings = template.match(this.getURI().getPath()); - if (logger.isDebugEnabled()){ - logger.debug("Mapped URI variables: " + uriVariableMappings); - } - } - uriVarsMap = uriVariableMappings; - } - - public Map getRequestParamMap() { - return requestParamMap; - } - - @SuppressWarnings("rawtypes") - public Map getUriVars() { - return uriVarsMap; - } - } } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java index 27ffcdd7a5..bd157f1ce3 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java @@ -16,87 +16,39 @@ package org.springframework.integration.http.inbound; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.List; import java.util.Map; import org.junit.Test; -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.support.ConversionServiceFactory; -import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; -import org.springframework.expression.spel.support.StandardTypeConverter; -import org.springframework.http.HttpHeaders; import org.springframework.integration.Message; -import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.MessagingException; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.core.MessageHandler; import org.springframework.integration.http.MockHttpServletRequest; -import org.springframework.integration.http.inbound.HttpRequestHandlingEndpointSupport.SmartServletServerHttpRequest; import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.util.CollectionUtils; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; /** * @author Oleg Zhurakousky */ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { - @Test - public void defaultUriVariableMappingWithPOST() throws Exception { - QueueChannel requestChannel = new QueueChannel(); - HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(false); - gateway.setPath("/fname/{f}/lname/{l}"); - gateway.setRequestChannel(requestChannel); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setMethod("POST"); - request.setContentType("text/plain"); - request.setParameter("foo", "bar"); - request.setContent("hello".getBytes()); - request.setRequestURI("/fname/bill/lname/clinton"); - MockHttpServletResponse response = new MockHttpServletResponse(); - gateway.handleRequest(request, response); - Message message = requestChannel.receive(0); - assertNotNull(message); - assertEquals("bill", message.getHeaders().get("f")); - assertEquals("clinton", message.getHeaders().get("l")); - } + private static ExpressionParser PARSER = new SpelExpressionParser(); + - @SuppressWarnings("rawtypes") @Test - public void defaultUriVariableMappingWithGET() throws Exception { - QueueChannel requestChannel = new QueueChannel(); - HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(false); - gateway.setPath("/fname/{f}/lname/{l}"); - gateway.setRequestChannel(requestChannel); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setMethod("GET"); - request.setParameter("foo", "bar"); - request.setRequestURI("/fname/bill/lname/clinton"); - MockHttpServletResponse response = new MockHttpServletResponse(); - gateway.handleRequest(request, response); - Message message = requestChannel.receive(0); - assertNotNull(message); - assertNull(message.getHeaders().get("f")); - assertNull(message.getHeaders().get("l")); - Map payload = (Map) message.getPayload(); - assertEquals(Collections.singletonList("bill"), payload.get("f")); - assertEquals(Collections.singletonList("clinton"), payload.get("l")); - } - /** - * This is a temporary test which simply shows what would happen inside of the gateway which has an internal - * capability for SpEL based extraction of data such as UriVariabe mappings, HttpHeaders, Body and Request Parameters. - * - * @throws Exception - */ - @Test - public void withExpression() throws Exception { + public void withoutExpression() throws Exception { + DirectChannel echoChannel = new DirectChannel(); + echoChannel.subscribe(new MessageHandler() { + + public void handleMessage(Message message) throws MessagingException { + MessageChannel replyChannel = (MessageChannel) message.getHeaders().getReplyChannel(); + replyChannel.send(message); + } + }); MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); request.setContentType("text/plain"); @@ -106,39 +58,78 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true); gateway.setPath("/fname/{f}/lname/{l}"); + gateway.setRequestChannel(echoChannel); - SmartServletServerHttpRequest smartRequest = gateway.new SmartServletServerHttpRequest(request); + MockHttpServletResponse response = new MockHttpServletResponse(); - StandardEvaluationContext context = new StandardEvaluationContext(); - ConversionService conversionService = ConversionServiceFactory.createDefaultConversionService(); + Object result = gateway.doHandleRequest(request, response); + assertEquals("hello", result); - context.setTypeConverter(new StandardTypeConverter(conversionService)); - - ExpressionParser parser = new SpelExpressionParser(); - - Expression bodyExpression = parser.parseExpression("body"); - InputStream body = bodyExpression.getValue(context, smartRequest, InputStream.class); - assertNotNull(body); - assertEquals("hello", this.convertToString(body)); - - Expression headersExpression = parser.parseExpression("headers['Content-Type']"); - List headers = headersExpression.getValue(context, smartRequest, List.class); - assertNotNull(headers); - assertEquals("text/plain", headers.get(0)); - - Expression uriVarsExpression = parser.parseExpression("uriVars['f']"); - String fname = uriVarsExpression.getValue(context, smartRequest, String.class); - assertNotNull(fname); - assertEquals("bill", fname); } - public String convertToString(InputStream in) throws IOException { - StringBuffer out = new StringBuffer(); - byte[] b = new byte[4096]; - for (int n; (n = in.read(b)) != -1;) { - out.append(new String(b, 0, n)); - } - return out.toString(); + @Test + public void withoutPayloadExpressionPointingToUriVariable() throws Exception { + + DirectChannel echoChannel = new DirectChannel(); + echoChannel.subscribe(new MessageHandler() { + + public void handleMessage(Message message) throws MessagingException { + MessageChannel replyChannel = (MessageChannel) message.getHeaders().getReplyChannel(); + replyChannel.send(message); + } + }); + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + + request.setMethod("POST"); + request.setContentType("text/plain"); + request.setParameter("foo", "bar"); + request.setContent("hello".getBytes()); + request.setRequestURI("/fname/bill/lname/clinton"); + + + + HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true); + gateway.setPath("/fname/{f}/lname/{l}"); + gateway.setRequestChannel(echoChannel); + gateway.setPayloadExpression(PARSER.parseExpression("#f")); + + Object result = gateway.doHandleRequest(request, response); + assertEquals("bill", result); + } + @Test + public void withoutPayloadExpressionPointingToUriVariables() throws Exception { + + DirectChannel echoChannel = new DirectChannel(); + echoChannel.subscribe(new MessageHandler() { + + public void handleMessage(Message message) throws MessagingException { + MessageChannel replyChannel = (MessageChannel) message.getHeaders().getReplyChannel(); + replyChannel.send(message); + } + }); + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + + request.setMethod("POST"); + request.setContentType("text/plain"); + request.setParameter("foo", "bar"); + request.setContent("hello".getBytes()); + request.setRequestURI("/fname/bill/lname/clinton"); + + + + HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true); + gateway.setPath("/fname/{f}/lname/{l}"); + gateway.setRequestChannel(echoChannel); + gateway.setPayloadExpression(PARSER.parseExpression("#uriVariables")); + + Object result = gateway.doHandleRequest(request, response); + assertEquals("bill", ((Map)result).get("f")); + + } + + } From 75b32e102271d1a492661a2e8f5b4f8a49e37a79 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Thu, 1 Sep 2011 15:15:32 -0400 Subject: [PATCH 06/12] INT-1677 fixed thread safety for EvaluatioinContext since it has to be created per request due to the fact that we are adding request data to it --- .../HttpRequestHandlingEndpointSupport.java | 46 +++++++++---------- ...gMessagingGatewayWithPathMappingTests.java | 11 ++--- 2 files changed, 24 insertions(+), 33 deletions(-) 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 083dfbb3b2..580fc23955 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 @@ -17,11 +17,8 @@ package org.springframework.integration.http.inbound; import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -33,13 +30,9 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.expression.BeanFactoryResolver; import org.springframework.context.expression.MapAccessor; import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.converter.Converter; import org.springframework.expression.Expression; -import org.springframework.expression.ExpressionParser; -import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardTypeConverter; -import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -126,8 +119,6 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor private volatile MultipartResolver multipartResolver; - private final StandardEvaluationContext evaluationContext; - private volatile Expression payloadExpression; private volatile Map headerExpressions; @@ -156,9 +147,6 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor // this.messageConverters.add(new AtomFeedHttpMessageConverter()); // this.messageConverters.add(new RssChannelHttpMessageConverter()); } - StandardEvaluationContext sec = new StandardEvaluationContext(); - sec.addPropertyAccessor(new MapAccessor()); - this.evaluationContext = sec; } /** @@ -276,13 +264,6 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor } } } - if (beanFactory != null) { - this.evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory)); - } - ConversionService conversionService = this.getConversionService(); - if (conversionService != null) { - this.evaluationContext.setTypeConverter(new StandardTypeConverter(conversionService)); - } } /** @@ -300,6 +281,8 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor } Map uriVariableMappings = null; // + StandardEvaluationContext evaluationContext = this.getEvaluationContext(); + if (StringUtils.hasText(this.path)){ UriTemplate template = new UriTemplate(this.path); uriVariableMappings = template.match(request.getURI().getPath()); @@ -309,10 +292,11 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor } // set the whole map - this.evaluationContext.setVariable("uriVariables", uriVariableMappings); + + evaluationContext.setVariable("uriVariables", uriVariableMappings); for (Object key : uriVariableMappings.keySet()) { // add individual elements - this.evaluationContext.setVariable((String) key, uriVariableMappings.get(key)); + evaluationContext.setVariable((String) key, uriVariableMappings.get(key)); } } else { @@ -327,12 +311,12 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor if (this.payloadExpression != null){ // create payload based on SpEL - payload = this.payloadExpression.getValue(this.evaluationContext, request); + payload = this.payloadExpression.getValue(evaluationContext, request); } if (this.headerExpressions != null){ for (String headerName : this.headerExpressions.keySet()) { Expression headerExpression = this.headerExpressions.get(headerName); - Object headerValue = headerExpression.getValue(this.evaluationContext, request); + Object headerValue = headerExpression.getValue(evaluationContext, request); ((Map)headers).put(headerName, headerValue); } } @@ -347,8 +331,6 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor } } - HttpEntity entity = new HttpEntity(request.getBody(), request.getHeaders()); - Message message = MessageBuilder.withPayload(payload).copyHeaders(headers).setHeader( org.springframework.integration.http.HttpHeaders.REQUEST_URL, request.getURI().toString()) .setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_METHOD, @@ -467,4 +449,18 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor } return httpStatus; } + + private StandardEvaluationContext getEvaluationContext(){ + StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); + evaluationContext.addPropertyAccessor(new MapAccessor()); + BeanFactory beanFactory = this.getBeanFactory(); + if (beanFactory != null) { + evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory)); + } + ConversionService conversionService = this.getConversionService(); + if (conversionService != null) { + evaluationContext.setTypeConverter(new StandardTypeConverter(conversionService)); + } + return evaluationContext; + } } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java index bd157f1ce3..81fd205e06 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java @@ -86,9 +86,7 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { request.setParameter("foo", "bar"); request.setContent("hello".getBytes()); request.setRequestURI("/fname/bill/lname/clinton"); - - - + HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true); gateway.setPath("/fname/{f}/lname/{l}"); gateway.setRequestChannel(echoChannel); @@ -99,6 +97,7 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { } + @SuppressWarnings("unchecked") @Test public void withoutPayloadExpressionPointingToUriVariables() throws Exception { @@ -119,17 +118,13 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { request.setContent("hello".getBytes()); request.setRequestURI("/fname/bill/lname/clinton"); - - HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true); gateway.setPath("/fname/{f}/lname/{l}"); gateway.setRequestChannel(echoChannel); gateway.setPayloadExpression(PARSER.parseExpression("#uriVariables")); Object result = gateway.doHandleRequest(request, response); - assertEquals("bill", ((Map)result).get("f")); - + assertEquals("bill", ((Map)result).get("f")); } - } From 7b1b2733936fb2083b0516dd76f53742171cccc9 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Thu, 1 Sep 2011 15:59:24 -0400 Subject: [PATCH 07/12] INT-1677 added UriPathHandlerMapping to aid ini support of 'path' attribute --- .../HttpRequestHandlingEndpointSupport.java | 6 +- .../http/inbound/UriPathHandlerMapping.java | 65 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 spring-integration-http/src/main/java/org/springframework/integration/http/inbound/UriPathHandlerMapping.java 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 580fc23955..fdb1a9dab7 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -159,6 +159,10 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor public void setPath(String path) { this.path = path; } + + String getPath() { + return path; + } public void setPayloadExpression(Expression payloadExpression) { this.payloadExpression = payloadExpression; diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/UriPathHandlerMapping.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/UriPathHandlerMapping.java new file mode 100644 index 0000000000..a6ad65e857 --- /dev/null +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/UriPathHandlerMapping.java @@ -0,0 +1,65 @@ +/* + * Copyright 2002-2011 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 + * + * http://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.util.Collections; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.util.ObjectUtils; +import org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping; + +/** + * @author Oleg Zhurakousky + * @since 2.1 + */ +public class UriPathHandlerMapping extends AbstractDetectingUrlHandlerMapping implements InitializingBean { + + protected void detectHandlers() throws BeansException { + if (logger.isDebugEnabled()) { + logger.debug("Looking for URL mappings in application context: " + getApplicationContext()); + } + + String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class); + + for (String beanName : beanNames) { + String[] urls = determineUrlsForHandler(beanName); + if (!ObjectUtils.isEmpty(urls)) { + // URL paths found: Let's consider it a handler. + registerHandler(urls, beanName); + } + else { + if (logger.isDebugEnabled()) { + logger.debug("Rejected bean name '" + beanName + "': no URL paths identified"); + } + } + } + } + + @Override + protected String[] determineUrlsForHandler(String beanName) { + ApplicationContext context = this.getApplicationContext(); + HttpRequestHandlingEndpointSupport handler = context.getBean(beanName, HttpRequestHandlingEndpointSupport.class); + String path = handler.getPath(); + return Collections.singletonList(path).toArray(new String[]{}); + } + + public void afterPropertiesSet() throws Exception { + this.setOrder(Integer.MIN_VALUE); + } +} From 90a593680356e4ed38831bd38e95ba321073d0a0 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Thu, 1 Sep 2011 16:28:09 -0400 Subject: [PATCH 08/12] INT-1677 updated 2.1 schema with new 'payload-expression' attribute as well as 'header' element. Polished UriPathHandlerMapping to address comments from the review --- .../http/inbound/UriPathHandlerMapping.java | 15 ++++--- .../config/spring-integration-http-2.1.xsd | 42 +++++++++++++++++++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/UriPathHandlerMapping.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/UriPathHandlerMapping.java index a6ad65e857..844e786fa9 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/UriPathHandlerMapping.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/UriPathHandlerMapping.java @@ -19,7 +19,6 @@ import java.util.Collections; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactoryUtils; -import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.util.ObjectUtils; import org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping; @@ -28,14 +27,18 @@ import org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMappin * @author Oleg Zhurakousky * @since 2.1 */ -public class UriPathHandlerMapping extends AbstractDetectingUrlHandlerMapping implements InitializingBean { - +public class UriPathHandlerMapping extends AbstractDetectingUrlHandlerMapping { + + public UriPathHandlerMapping(){ + this.setOrder(Integer.MIN_VALUE); + } + protected void detectHandlers() throws BeansException { if (logger.isDebugEnabled()) { logger.debug("Looking for URL mappings in application context: " + getApplicationContext()); } - String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class); + String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), HttpRequestHandlingEndpointSupport.class); for (String beanName : beanNames) { String[] urls = determineUrlsForHandler(beanName); @@ -58,8 +61,4 @@ public class UriPathHandlerMapping extends AbstractDetectingUrlHandlerMapping im String path = handler.getPath(); return Collections.singletonList(path).toArray(new String[]{}); } - - public void afterPropertiesSet() throws Exception { - this.setOrder(Integer.MIN_VALUE); - } } diff --git a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.1.xsd b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.1.xsd index 92a827ea1c..5b3c1615df 100644 --- a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.1.xsd +++ b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.1.xsd @@ -20,6 +20,9 @@ Defines an inbound HTTP-based Channel Adapter. + + + @@ -57,6 +60,13 @@ + + + + Allows you to specify SpEL expression to construct a Message payload + + + @@ -116,6 +126,9 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re + + + @@ -135,6 +148,13 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re + + + + Allows you to specify SpEL expression to construct a Message payload + + + @@ -493,6 +513,28 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re + + + + + + + + + + + + + + + + From a6c85c1dd91cc62632aa4d3ec9227a2d36a2a3f3 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Fri, 2 Sep 2011 08:26:13 -0400 Subject: [PATCH 09/12] INT-1677 finished the namespace support for payload-expression attribute and header sub-element, added parser test --- .../config/HttpInboundEndpointParser.java | 35 +++++++++++++++++++ .../config/spring-integration-http-2.1.xsd | 14 ++++++++ ...boundChannelAdapterParserTests-context.xml | 8 +++++ .../HttpInboundChannelAdapterParserTests.java | 24 +++++++++++++ 4 files changed, 81 insertions(+) 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 f99f8f5f80..762d62284e 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 @@ -16,13 +16,22 @@ package org.springframework.integration.http.config; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.ManagedMap; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.ExpressionFactoryBean; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; @@ -76,6 +85,32 @@ public class HttpInboundEndpointParser extends AbstractSingleBeanDefinitionParse } builder.addPropertyReference("requestChannel", inputChannelRef); IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "error-channel"); + + + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "path"); + String payloadExpression = element.getAttribute("payload-expression"); + if (StringUtils.hasText(payloadExpression)){ + RootBeanDefinition expressionDef = new RootBeanDefinition(ExpressionFactoryBean.class); + expressionDef.getConstructorArgumentValues().addGenericArgumentValue(payloadExpression); + builder.addPropertyValue("payloadExpression", expressionDef); + } + + List headerElements = DomUtils.getChildElementsByTagName(element, "header"); + if (!CollectionUtils.isEmpty(headerElements)) { + + ManagedMap headerElementsMap = new ManagedMap(); + for (Element headerElement : headerElements) { + String name = headerElement.getAttribute("name"); + String expression = headerElement.getAttribute("expression"); + if (StringUtils.hasText(expression)){ + RootBeanDefinition expressionDef = new RootBeanDefinition(ExpressionFactoryBean.class); + expressionDef.getConstructorArgumentValues().addGenericArgumentValue(expression); + headerElementsMap.put(name, expressionDef); + } + } + builder.addPropertyValue("headerExpressions", headerElementsMap); + } + if (this.expectReply) { IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "request-timeout"); diff --git a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.1.xsd b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.1.xsd index 5b3c1615df..d07ee90c78 100644 --- a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.1.xsd +++ b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.1.xsd @@ -67,6 +67,13 @@ + + + + Allows you to specify URI path (e.g., /orderId/{order}) + + + @@ -155,6 +162,13 @@ The String "HTTP_REQUEST_HEADERS" will match against any of the standard HTTP Re + + + + Allows you to specify URI path (e.g., /orderId/{order}) + + + 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 4dc44dabb2..d83435343d 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 @@ -26,5 +26,13 @@ + + +
+ 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 18a0252235..6a8aa31e59 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 @@ -72,6 +72,9 @@ public class HttpInboundChannelAdapterParserTests { @Autowired private HttpRequestHandlingMessagingGateway withMappedHeaders; + + @Autowired + private HttpRequestHandlingMessagingGateway inboundAdapterWithExpressions; @Autowired private HttpRequestHandlingController inboundController; @@ -113,6 +116,27 @@ public class HttpInboundChannelAdapterParserTests { assertEquals("foo", map.get("foo")); assertEquals("bar", map.get("bar")); } + + @Test + @SuppressWarnings("unchecked")// INT-1677 + public void withExpressions() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("POST"); + request.setContentType("text/plain"); + request.setParameter("foo", "bar"); + request.setContent("hello".getBytes()); + request.setRequestURI("/fname/bill/lname/clinton"); + + MockHttpServletResponse response = new MockHttpServletResponse(); + inboundAdapterWithExpressions.handleRequest(request, response); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + Message message = requests.receive(0); + assertNotNull(message); + Object payload = message.getPayload(); + assertTrue(payload instanceof String); + assertEquals("bill", payload); + assertEquals("clinton", message.getHeaders().get("lname")); + } @Test public void getRequestNotAllowed() throws Exception { From c07d62dadbc92c1161d5214499bceed10c47456f Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Fri, 2 Sep 2011 09:56:44 -0400 Subject: [PATCH 10/12] INT-1677 removed requirement for anID, polished schema, added more tests, tested with server --- .../config/HttpInboundEndpointParser.java | 9 ++- .../HttpRequestHandlingEndpointSupport.java | 7 +-- .../config/spring-integration-http-2.1.xsd | 9 ++- ...boundChannelAdapterParserTests-context.xml | 15 +++++ .../HttpInboundChannelAdapterParserTests.java | 56 ++++++++++++++++++- 5 files changed, 84 insertions(+), 12 deletions(-) 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 762d62284e..8a648d0816 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 @@ -16,13 +16,12 @@ package org.springframework.integration.http.config; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.ManagedMap; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; @@ -32,7 +31,6 @@ import org.springframework.integration.config.xml.IntegrationNamespaceUtils; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; - import org.w3c.dom.Element; /** @@ -69,8 +67,9 @@ public class HttpInboundEndpointParser extends AbstractSingleBeanDefinitionParse id = element.getAttribute("name"); } if (!StringUtils.hasText(id)) { - parserContext.getReaderContext().error("The 'id' or 'name' is required.", element); + id = BeanDefinitionReaderUtils.generateBeanName(definition, parserContext.getRegistry()); } + return id; } @@ -96,8 +95,8 @@ public class HttpInboundEndpointParser extends AbstractSingleBeanDefinitionParse } List headerElements = DomUtils.getChildElementsByTagName(element, "header"); + if (!CollectionUtils.isEmpty(headerElements)) { - ManagedMap headerElementsMap = new ManagedMap(); for (Element headerElement : headerElements) { String name = headerElement.getAttribute("name"); 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 fdb1a9dab7..e88d8a7317 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 @@ -285,7 +285,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor } Map uriVariableMappings = null; // - StandardEvaluationContext evaluationContext = this.getEvaluationContext(); + StandardEvaluationContext evaluationContext = this.prepareAndGetEvaluationContext(); if (StringUtils.hasText(this.path)){ UriTemplate template = new UriTemplate(this.path); @@ -295,8 +295,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor logger.debug("Mapped URI variables: " + uriVariableMappings); } - // set the whole map - + // set the whole map evaluationContext.setVariable("uriVariables", uriVariableMappings); for (Object key : uriVariableMappings.keySet()) { // add individual elements @@ -454,7 +453,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor return httpStatus; } - private StandardEvaluationContext getEvaluationContext(){ + private StandardEvaluationContext prepareAndGetEvaluationContext(){ StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); evaluationContext.addPropertyAccessor(new MapAccessor()); BeanFactory beanFactory = this.getBeanFactory(); diff --git a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.1.xsd b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.1.xsd index d07ee90c78..a39372fd6d 100644 --- a/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.1.xsd +++ b/spring-integration-http/src/main/resources/org/springframework/integration/http/config/spring-integration-http-2.1.xsd @@ -24,7 +24,14 @@ - + + + + [DEPRECATED since v2.1] Use 'path' attribute if you want to specify the path or + 'id' attribute if you simply want to identify this component + + + 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 d83435343d..e00725864f 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 @@ -34,5 +34,20 @@ payload-expression="#f">
+ + +
+ + + +
+ 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 6a8aa31e59..12035744ac 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 @@ -35,6 +35,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.integration.Message; @@ -75,7 +76,15 @@ public class HttpInboundChannelAdapterParserTests { @Autowired private HttpRequestHandlingMessagingGateway inboundAdapterWithExpressions; - + + @Autowired + @Qualifier("/fname/{blah}/lname/{boo}") + private HttpRequestHandlingMessagingGateway inboundAdapterWithNameAndExpressions; + + @Autowired + @Qualifier("/fname/{f}/lname/{l}") + private HttpRequestHandlingMessagingGateway inboundAdapterWithNameNoPath; + @Autowired private HttpRequestHandlingController inboundController; @@ -118,7 +127,7 @@ public class HttpInboundChannelAdapterParserTests { } @Test - @SuppressWarnings("unchecked")// INT-1677 + // INT-1677 public void withExpressions() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); @@ -137,6 +146,49 @@ public class HttpInboundChannelAdapterParserTests { assertEquals("bill", payload); assertEquals("clinton", message.getHeaders().get("lname")); } + @Test // ensure that 'path' takes priority over name + // INT-1677 + public void withNameAndExpressionsAndPath() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("POST"); + request.setContentType("text/plain"); + request.setParameter("foo", "bar"); + request.setContent("hello".getBytes()); + request.setRequestURI("/fname/bill/lname/clinton"); + + MockHttpServletResponse response = new MockHttpServletResponse(); + inboundAdapterWithNameAndExpressions.handleRequest(request, response); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + Message message = requests.receive(0); + assertNotNull(message); + Object payload = message.getPayload(); + assertTrue(payload instanceof String); + assertEquals("bill", payload); + assertEquals("clinton", message.getHeaders().get("lname")); + } + + @Test + // INT-1677 + public void withNameAndExpressionsNoPath() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("POST"); + request.setContentType("text/plain"); + request.setParameter("foo", "bar"); + request.setContent("hello".getBytes()); + request.setRequestURI("/fname/bill/lname/clinton"); + + MockHttpServletResponse response = new MockHttpServletResponse(); + inboundAdapterWithNameNoPath.handleRequest(request, response); + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + Message message = requests.receive(0); + assertNotNull(message); + Object payload = message.getPayload(); + assertTrue(payload instanceof String); + assertEquals("hello", payload); // default payload + assertNull(message.getHeaders().get("lname")); + } + + @Test public void getRequestNotAllowed() throws Exception { From 581f21f3ebf510bb957e392bd61b68bca653dd98 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Fri, 2 Sep 2011 12:17:21 -0400 Subject: [PATCH 11/12] INT-1677 changed uriVariables to pathVariables, added parameterMap and parameters as well as HttpEntity to the EvauationContext --- .../HttpRequestHandlingEndpointSupport.java | 29 +++++++++++++++---- ...gMessagingGatewayWithPathMappingTests.java | 4 +-- 2 files changed, 25 insertions(+), 8 deletions(-) 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 e88d8a7317..09b385e8b4 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 @@ -33,6 +33,7 @@ import org.springframework.core.convert.ConversionService; import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardTypeConverter; +import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -285,7 +286,23 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor } Map uriVariableMappings = null; // - StandardEvaluationContext evaluationContext = this.prepareAndGetEvaluationContext(); + StandardEvaluationContext evaluationContext = this.createEvaluationContext(); + + Object requestBody = null; + if (this.isReadable(request)) { + requestBody = this.extractRequestBody(request); + evaluationContext.setVariable("requestBody", requestBody); + } + + HttpEntity httpEntity = new HttpEntity(requestBody, request.getHeaders()); + evaluationContext.setRootObject(httpEntity); + + LinkedMultiValueMap requestParameterMap = this.convertParameterMap(servletRequest.getParameterMap()); + evaluationContext.setVariable("requestParameterMap", uriVariableMappings);// bind the whole map + + for (String parameterName : requestParameterMap.keySet()) { // bind individual parameters + evaluationContext.setVariable(parameterName, requestParameterMap.get(parameterName)); + } if (StringUtils.hasText(this.path)){ UriTemplate template = new UriTemplate(this.path); @@ -296,7 +313,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor } // set the whole map - evaluationContext.setVariable("uriVariables", uriVariableMappings); + evaluationContext.setVariable("pathVariables", uriVariableMappings); for (Object key : uriVariableMappings.keySet()) { // add individual elements evaluationContext.setVariable((String) key, uriVariableMappings.get(key)); @@ -326,11 +343,11 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor if (payload == null){ if (this.isReadable(request)) { - payload = this.extractRequestBody(request); + //payload = this.extractRequestBody(request); + payload = requestBody; } else { - payload = this.convertParameterMap(servletRequest.getParameterMap()); - + payload = requestParameterMap; } } @@ -453,7 +470,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor return httpStatus; } - private StandardEvaluationContext prepareAndGetEvaluationContext(){ + private StandardEvaluationContext createEvaluationContext(){ StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); evaluationContext.addPropertyAccessor(new MapAccessor()); BeanFactory beanFactory = this.getBeanFactory(); diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java index 81fd205e06..1071a98c9b 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2011 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. @@ -121,7 +121,7 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true); gateway.setPath("/fname/{f}/lname/{l}"); gateway.setRequestChannel(echoChannel); - gateway.setPayloadExpression(PARSER.parseExpression("#uriVariables")); + gateway.setPayloadExpression(PARSER.parseExpression("#pathVariables")); Object result = gateway.doHandleRequest(request, response); assertEquals("bill", ((Map)result).get("f")); From b4c210691c2808d6030e2ea831b66f8ec811a2d9 Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Fri, 2 Sep 2011 16:01:37 -0400 Subject: [PATCH 12/12] polishing --- .../integration/mapping/HeaderMapper.java | 2 +- .../HttpRequestHandlingEndpointSupport.java | 134 ++++++++++-------- .../http/inbound/UriPathHandlerMapping.java | 52 +++---- ...boundChannelAdapterParserTests-context.xml | 12 +- .../HttpInboundChannelAdapterParserTests.java | 6 +- ...gMessagingGatewayWithPathMappingTests.java | 13 +- 6 files changed, 105 insertions(+), 114 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/mapping/HeaderMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/mapping/HeaderMapper.java index 70b010d150..ba3fdeba1f 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/mapping/HeaderMapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/mapping/HeaderMapper.java @@ -33,6 +33,6 @@ public interface HeaderMapper { void fromHeaders(MessageHeaders headers, T target); - Map toHeaders(T source); + Map toHeaders(T source); } 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 09b385e8b4..90b7f8b7a2 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 @@ -57,15 +57,18 @@ import org.springframework.integration.http.multipart.MultipartHttpInputMessage; import org.springframework.integration.http.support.DefaultHttpHeaderMapper; import org.springframework.integration.mapping.HeaderMapper; import org.springframework.integration.support.MessageBuilder; +import org.springframework.util.AntPathMatcher; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.util.PathMatcher; import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartHttpServletRequest; import org.springframework.web.multipart.MultipartResolver; import org.springframework.web.servlet.DispatcherServlet; -import org.springframework.web.util.UriTemplate; +import org.springframework.web.util.UrlPathHelper; /** * Base class for HTTP request handling endpoints. @@ -116,6 +119,10 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor private volatile String path; + private final UrlPathHelper urlPathHelper = new UrlPathHelper(); + + private final PathMatcher pathMatcher = new AntPathMatcher(); + private volatile boolean extractReplyPayload = true; private volatile MultipartResolver multipartResolver; @@ -156,7 +163,11 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor protected boolean isExpectReply() { return expectReply; } - + + /** + * Set the path template for which this endpoint expects requests. + * May include path variable {keys} to match against. + */ public void setPath(String path) { this.path = path; } @@ -165,13 +176,27 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor return path; } + /** + * Specifies a SpEL expression to evaluate in order to generate the Message payload. + * The EvaluationContext will be populated with an HttpEntity instance as the root object, + * and it may contain one or both of the #pathVariables and + * #queryParameters variables if present. Those variables' values are Maps. + */ public void setPayloadExpression(Expression payloadExpression) { this.payloadExpression = payloadExpression; } + /** + * Specifies a Map of SpEL expressions to evaluate in order to generate the Message headers. + * The keys in the map will be used as the header names. When evaluating the expression, + * the EvaluationContext will be populated with an HttpEntity instance as the root object, + * and it may contain one or both of the #pathVariables and + * #queryParameters variables if present. Those variables' values are Maps. + */ public void setHeaderExpressions(Map headerExpressions) { this.headerExpressions = headerExpressions; } + /** * Set the message body converters to use. These converters are used to convert from and to HTTP requests and * responses. @@ -276,87 +301,70 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor * 'expectReply' property is true, it will also generate a response from the reply Message once received. */ @SuppressWarnings({ "rawtypes", "unchecked" }) - protected final Object doHandleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse) - throws IOException { + protected final Object doHandleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException { try { ServletServerHttpRequest request = this.prepareRequest(servletRequest); if (!this.supportedMethods.contains(request.getMethod())) { servletResponse.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); return null; } - Map uriVariableMappings = null; - // - StandardEvaluationContext evaluationContext = this.createEvaluationContext(); - + Object requestBody = null; if (this.isReadable(request)) { requestBody = this.extractRequestBody(request); - evaluationContext.setVariable("requestBody", requestBody); } - HttpEntity httpEntity = new HttpEntity(requestBody, request.getHeaders()); + + StandardEvaluationContext evaluationContext = this.createEvaluationContext(); evaluationContext.setRootObject(httpEntity); - - LinkedMultiValueMap requestParameterMap = this.convertParameterMap(servletRequest.getParameterMap()); - evaluationContext.setVariable("requestParameterMap", uriVariableMappings);// bind the whole map - - for (String parameterName : requestParameterMap.keySet()) { // bind individual parameters - evaluationContext.setVariable(parameterName, requestParameterMap.get(parameterName)); - } - - if (StringUtils.hasText(this.path)){ - UriTemplate template = new UriTemplate(this.path); - uriVariableMappings = template.match(request.getURI().getPath()); - if (!uriVariableMappings.isEmpty()){ - if (logger.isDebugEnabled()){ - logger.debug("Mapped URI variables: " + uriVariableMappings); - } - - // set the whole map - evaluationContext.setVariable("pathVariables", uriVariableMappings); - for (Object key : uriVariableMappings.keySet()) { - // add individual elements - evaluationContext.setVariable((String) key, uriVariableMappings.get(key)); + + LinkedMultiValueMap requestParams = this.convertParameterMap(servletRequest.getParameterMap()); + evaluationContext.setVariable("requestParams", requestParams); + + if (StringUtils.hasText(this.path)) { + String lookupPath = this.urlPathHelper.getLookupPathForRequest(servletRequest); + Map pathVariables = this.pathMatcher.extractUriTemplateVariables(this.path, lookupPath); + if (!pathVariables.isEmpty()) { + if (logger.isDebugEnabled()) { + logger.debug("Mapped path variables: " + pathVariables); } - } - else { - logger.warn("Was not able to match UriVariables to path: " + this.path); - } - } - // - - Map headers = this.headerMapper.toHeaders(request.getHeaders()); - - Object payload = null; - - if (this.payloadExpression != null){ - // create payload based on SpEL - payload = this.payloadExpression.getValue(evaluationContext, request); - } - if (this.headerExpressions != null){ - for (String headerName : this.headerExpressions.keySet()) { - Expression headerExpression = this.headerExpressions.get(headerName); - Object headerValue = headerExpression.getValue(evaluationContext, request); - ((Map)headers).put(headerName, headerValue); + evaluationContext.setVariable("pathVariables", pathVariables); } - } - - if (payload == null){ - if (this.isReadable(request)) { - //payload = this.extractRequestBody(request); + } + + Map headers = this.headerMapper.toHeaders(request.getHeaders()); + Object payload = null; + if (this.payloadExpression != null) { + // create payload based on SpEL + payload = this.payloadExpression.getValue(evaluationContext); + } + if (!CollectionUtils.isEmpty(this.headerExpressions)) { + for (String headerName : this.headerExpressions.keySet()) { + Expression headerExpression = this.headerExpressions.get(headerName); + Object headerValue = headerExpression.getValue(evaluationContext); + if (headerValue != null) { + headers.put(headerName, headerValue); + } + } + } + + if (payload == null) { + if (requestBody != null) { payload = requestBody; } else { - payload = requestParameterMap; + payload = requestParams; } } - - Message message = MessageBuilder.withPayload(payload).copyHeaders(headers).setHeader( - org.springframework.integration.http.HttpHeaders.REQUEST_URL, request.getURI().toString()) + + Message message = MessageBuilder.withPayload(payload).copyHeaders(headers) + .setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_URL, + request.getURI().toString()) .setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_METHOD, - request.getMethod().toString()).setHeader( - org.springframework.integration.http.HttpHeaders.USER_PRINCIPAL, + request.getMethod().toString()) + .setHeader(org.springframework.integration.http.HttpHeaders.USER_PRINCIPAL, servletRequest.getUserPrincipal()).build(); + Object reply = null; if (this.expectReply) { reply = this.sendAndReceiveMessage(message); @@ -364,7 +372,7 @@ abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewaySuppor ServletServerHttpResponse response = new ServletServerHttpResponse(servletResponse); this.headerMapper.fromHeaders(((Message) reply).getHeaders(), response.getHeaders()); HttpStatus httpStatus = this.resolveHttpStatusFromHeaders(((Message) reply).getHeaders()); - if (httpStatus != null){ + if (httpStatus != null) { response.setStatusCode(httpStatus); } response.close(); diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/UriPathHandlerMapping.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/UriPathHandlerMapping.java index 844e786fa9..971f11abd9 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/UriPathHandlerMapping.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/UriPathHandlerMapping.java @@ -13,52 +13,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.integration.http.inbound; -import java.util.Collections; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactoryUtils; -import org.springframework.context.ApplicationContext; -import org.springframework.util.ObjectUtils; import org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping; /** + * A {@link org.springframework.web.servlet.HandlerMapping} implementation that matches + * against the value of the 'path' attribute, if present, on a Spring Integration HTTP + * <inbound-channel-adapter> or <inbound-gateway> element. + * * @author Oleg Zhurakousky + * @author Mark Fisher * @since 2.1 */ public class UriPathHandlerMapping extends AbstractDetectingUrlHandlerMapping { - - public UriPathHandlerMapping(){ - this.setOrder(Integer.MIN_VALUE); - } - - protected void detectHandlers() throws BeansException { - if (logger.isDebugEnabled()) { - logger.debug("Looking for URL mappings in application context: " + getApplicationContext()); - } - - String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), HttpRequestHandlingEndpointSupport.class); - - for (String beanName : beanNames) { - String[] urls = determineUrlsForHandler(beanName); - if (!ObjectUtils.isEmpty(urls)) { - // URL paths found: Let's consider it a handler. - registerHandler(urls, beanName); - } - else { - if (logger.isDebugEnabled()) { - logger.debug("Rejected bean name '" + beanName + "': no URL paths identified"); - } - } - } - } @Override protected String[] determineUrlsForHandler(String beanName) { - ApplicationContext context = this.getApplicationContext(); - HttpRequestHandlingEndpointSupport handler = context.getBean(beanName, HttpRequestHandlingEndpointSupport.class); - String path = handler.getPath(); - return Collections.singletonList(path).toArray(new String[]{}); + String[] urls = null; + Class beanClass = getApplicationContext().getType(beanName); + if (HttpRequestHandlingEndpointSupport.class.isAssignableFrom(beanClass)) { + HttpRequestHandlingEndpointSupport endpoint = getApplicationContext().getBean(beanName, HttpRequestHandlingEndpointSupport.class); + String path = endpoint.getPath(); + if (path != null) { + urls = new String[]{path}; + } + } + return urls; } + } 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 e00725864f..dff2dee5a0 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 @@ -31,23 +31,23 @@ path="/fname/{f}/lname/{l}" channel="requests" mapped-request-headers="foo,bar" - payload-expression="#f"> -
+ payload-expression="#pathVariables.f"> +
-
+ payload-expression="#pathVariables.f"> +
-
+ payload-expression="#pathVariables.f"> +
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 12035744ac..47b6b4eb47 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 @@ -36,6 +36,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.integration.Message; @@ -47,6 +48,7 @@ import org.springframework.integration.http.inbound.HttpRequestHandlingMessaging import org.springframework.integration.http.support.DefaultHttpHeaderMapper; import org.springframework.integration.test.util.TestUtils; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.annotation.ExpectedException; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.MultiValueMap; @@ -146,6 +148,7 @@ public class HttpInboundChannelAdapterParserTests { assertEquals("bill", payload); assertEquals("clinton", message.getHeaders().get("lname")); } + @Test // ensure that 'path' takes priority over name // INT-1677 public void withNameAndExpressionsAndPath() throws Exception { @@ -167,8 +170,9 @@ public class HttpInboundChannelAdapterParserTests { assertEquals("clinton", message.getHeaders().get("lname")); } - @Test + @Test // INT-1677 + @ExpectedException(SpelEvaluationException.class) public void withNameAndExpressionsNoPath() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); request.setMethod("POST"); diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java index 1071a98c9b..dd9648cf51 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java @@ -68,11 +68,9 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { } @Test - public void withoutPayloadExpressionPointingToUriVariable() throws Exception { - + public void withPayloadExpressionPointingToPathVariable() throws Exception { DirectChannel echoChannel = new DirectChannel(); echoChannel.subscribe(new MessageHandler() { - public void handleMessage(Message message) throws MessagingException { MessageChannel replyChannel = (MessageChannel) message.getHeaders().getReplyChannel(); replyChannel.send(message); @@ -80,21 +78,20 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests { }); MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); - + request.setMethod("POST"); request.setContentType("text/plain"); request.setParameter("foo", "bar"); request.setContent("hello".getBytes()); request.setRequestURI("/fname/bill/lname/clinton"); - + HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true); gateway.setPath("/fname/{f}/lname/{l}"); gateway.setRequestChannel(echoChannel); - gateway.setPayloadExpression(PARSER.parseExpression("#f")); - + gateway.setPayloadExpression(PARSER.parseExpression("#pathVariables.f")); + Object result = gateway.doHandleRequest(request, response); assertEquals("bill", result); - } @SuppressWarnings("unchecked")