INT-1362 HTTP Inbound: Additional EvalCtx Vars.
JIRA: https://jira.springsource.org/browse/INT-1362, https://jira.springsource.org/browse/INT-2469 * Add to the `EvaluationContext` of HTTP Inbound Endpoints additional request variables * Tests and documentation INT-2669: Mention SpEL variables in JavaDocs JIRA: https://jira.springsource.org/browse/INT-2669 INT-1362 Doc Polishing
This commit is contained in:
committed by
Gary Russell
parent
d06c1c269d
commit
d5e8e352b6
@@ -20,12 +20,14 @@ import java.util.List;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
@@ -33,14 +35,13 @@ import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.config.ExpressionFactoryBean;
|
||||
import org.springframework.integration.config.SpelFunctionFactoryBean;
|
||||
import org.springframework.integration.endpoint.AbstractPollingEndpoint;
|
||||
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
@@ -462,6 +463,7 @@ public abstract class IntegrationNamespaceUtils {
|
||||
}
|
||||
return expressionDef;
|
||||
}
|
||||
|
||||
public static void registerSpelFunctionBean(BeanDefinitionRegistry registry, String functionId, String className,
|
||||
String methodSignature) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SpelFunctionFactoryBean.class)
|
||||
@@ -469,6 +471,7 @@ public abstract class IntegrationNamespaceUtils {
|
||||
.addConstructorArgValue(methodSignature);
|
||||
registry.registerBeanDefinition(functionId, builder.getBeanDefinition());
|
||||
}
|
||||
|
||||
public static BeanDefinition createExpressionDefIfAttributeDefined(String expressionElementName, Element element) {
|
||||
|
||||
Assert.hasText(expressionElementName, "'expressionElementName' must no be empty");
|
||||
|
||||
0
spring-integration-ftp/test/A.TEST.a
Normal file
0
spring-integration-ftp/test/A.TEST.a
Normal file
0
spring-integration-ftp/test/B.TEST.a
Normal file
0
spring-integration-ftp/test/B.TEST.a
Normal file
@@ -19,10 +19,12 @@ package org.springframework.integration.http.inbound;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@@ -65,6 +67,7 @@ import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
import org.springframework.web.multipart.MultipartResolver;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
@@ -210,8 +213,15 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa
|
||||
/**
|
||||
* 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 <code>#pathVariables</code> and
|
||||
* <code>#queryParameters</code> variables if present. Those variables' values are Maps.
|
||||
* and it may contain variables:
|
||||
* <ul>
|
||||
* <li><code>#pathVariables</code></li>
|
||||
* <li><code>#requestParams</code></li>
|
||||
* <li><code>#requestAttributes</code></li>
|
||||
* <li><code>#requestHeaders</code></li>
|
||||
* <li><code>#matrixVariables</code></li>
|
||||
* <li><code>#cookies</code>
|
||||
* </ul>
|
||||
*/
|
||||
public void setPayloadExpression(Expression payloadExpression) {
|
||||
this.payloadExpression = payloadExpression;
|
||||
@@ -221,8 +231,15 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa
|
||||
* 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 <code>#pathVariables</code> and
|
||||
* <code>#queryParameters</code> variables if present. Those variables' values are Maps.
|
||||
* and it may contain variables:
|
||||
* <ul>
|
||||
* <li><code>#pathVariables</code></li>
|
||||
* <li><code>#requestParams</code></li>
|
||||
* <li><code>#requestAttributes</code></li>
|
||||
* <li><code>#requestHeaders</code></li>
|
||||
* <li><code>#matrixVariables</code></li>
|
||||
* <li><code>#cookies</code>
|
||||
* </ul>
|
||||
*/
|
||||
public void setHeaderExpressions(Map<String, Expression> headerExpressions) {
|
||||
this.headerExpressions = headerExpressions;
|
||||
@@ -379,9 +396,22 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa
|
||||
StandardEvaluationContext evaluationContext = this.createEvaluationContext();
|
||||
evaluationContext.setRootObject(httpEntity);
|
||||
|
||||
evaluationContext.setVariable("requestAttributes", RequestContextHolder.currentRequestAttributes());
|
||||
|
||||
MultiValueMap<String, String> requestParams = this.convertParameterMap(servletRequest.getParameterMap());
|
||||
evaluationContext.setVariable("requestParams", requestParams);
|
||||
|
||||
evaluationContext.setVariable("requestHeaders", new ServletServerHttpRequest(servletRequest).getHeaders());
|
||||
|
||||
Cookie[] requestCookies = servletRequest.getCookies();
|
||||
if (!ObjectUtils.isEmpty(requestCookies)) {
|
||||
Map<String, Cookie> cookies = new HashMap<String, Cookie>(requestCookies.length);
|
||||
for (Cookie requestCookie : requestCookies) {
|
||||
cookies.put(requestCookie.getName(), requestCookie);
|
||||
}
|
||||
evaluationContext.setVariable("cookies", cookies);
|
||||
}
|
||||
|
||||
Map<String, String> pathVariables =
|
||||
(Map<String, String>) servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
|
||||
@@ -392,6 +422,17 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa
|
||||
evaluationContext.setVariable("pathVariables", pathVariables);
|
||||
}
|
||||
|
||||
//TODO change it to HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE after upgrade to Spring 4.0
|
||||
Map<String, MultiValueMap<String, String>> matrixVariables =
|
||||
(Map<String, MultiValueMap<String, String>>) servletRequest.getAttribute(HandlerMapping.class.getName() + ".matrixVariables");
|
||||
|
||||
if (!CollectionUtils.isEmpty(matrixVariables)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Mapped matrix variables: " + matrixVariables);
|
||||
}
|
||||
evaluationContext.setVariable("matrixVariables", matrixVariables);
|
||||
}
|
||||
|
||||
Map<String, Object> headers = this.headerMapper.toHeaders(request.getHeaders());
|
||||
Object payload = null;
|
||||
if (this.payloadExpression != null) {
|
||||
|
||||
@@ -26,8 +26,16 @@
|
||||
request-channel="toLowerCaseChannel"
|
||||
payload-expression="#pathVariables.value">
|
||||
<int-http:request-mapping headers="toLowerCase"/>
|
||||
|
||||
<int-http:header name="requestAttributes" expression="#requestAttributes"/>
|
||||
<int-http:header name="requestParams" expression="#requestParams"/>
|
||||
<int-http:header name="requestHeaders" expression="#requestHeaders"/>
|
||||
<int-http:header name="matrixVariables" expression="#matrixVariables"/>
|
||||
<int-http:header name="cookies" expression="#cookies"/>
|
||||
</int-http:inbound-gateway>
|
||||
|
||||
<int:publish-subscribe-channel id="toLowerCaseChannel"/>
|
||||
|
||||
<int:transformer input-channel="toLowerCaseChannel" expression="payload.toLowerCase()"/>
|
||||
|
||||
<int-http:inbound-gateway path="#{TEST_PATH}"
|
||||
|
||||
@@ -17,22 +17,36 @@
|
||||
package org.springframework.integration.http.inbound;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageHeaders;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.core.MessageHandler;
|
||||
import org.springframework.integration.core.SubscribableChannel;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.UnsatisfiedServletRequestParameterException;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.servlet.HandlerAdapter;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
@@ -53,6 +67,9 @@ public class Int2312RequestMappingIntegrationTests {
|
||||
@Autowired
|
||||
private HandlerMapping handlerMapping;
|
||||
|
||||
@Autowired
|
||||
private SubscribableChannel toLowerCaseChannel;
|
||||
|
||||
private HandlerAdapter handlerAdapter = new HttpRequestHandlerAdapter();
|
||||
|
||||
@Test
|
||||
@@ -77,22 +94,57 @@ public class Int2312RequestMappingIntegrationTests {
|
||||
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
//INT-1362
|
||||
public void testURIVariablesAndHeaders() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setMethod("GET");
|
||||
String testRequest = "aBc";
|
||||
// TODO test it after upgrade to Spring 4.0
|
||||
// String testRequest = "aBc;q1=1;q2=2";
|
||||
String requestURI = "/test/" + testRequest;
|
||||
request.setRequestURI(requestURI);
|
||||
request.setContentType("text/plain");
|
||||
final Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("foo", "bar");
|
||||
request.setParameters(params);
|
||||
request.setContent("hello".getBytes());
|
||||
final Cookie cookie = new Cookie("foo", "bar");
|
||||
request.setCookies(cookie);
|
||||
request.addHeader("toLowerCase", true);
|
||||
|
||||
//See org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#handleMatch
|
||||
Map<String, String> uriTemplateVariables =
|
||||
new AntPathMatcher().extractUriTemplateVariables(TEST_PATH, requestURI);
|
||||
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
|
||||
//See org.springframework.web.servlet.FrameworkServlet#initContextHolders
|
||||
final RequestAttributes attributes = new ServletRequestAttributes(request);
|
||||
RequestContextHolder.setRequestAttributes(attributes);
|
||||
|
||||
this.toLowerCaseChannel.subscribe(new MessageHandler() {
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
MessageHeaders headers = message.getHeaders();
|
||||
|
||||
assertEquals(attributes, headers.get("requestAttributes"));
|
||||
|
||||
Object requestParams = headers.get("requestParams");
|
||||
assertNotNull(requestParams);
|
||||
assertEquals(params, ((MultiValueMap<String, String>) requestParams).toSingleValueMap());
|
||||
|
||||
// TODO test it after upgrade to Spring 4.0
|
||||
// assertEquals(matrixVariables, headers.get("matrixVariables"));
|
||||
|
||||
Object requestHeaders = headers.get("requestHeaders");
|
||||
assertNotNull(requestParams);
|
||||
assertEquals(MediaType.TEXT_PLAIN, ((HttpHeaders) requestHeaders).getContentType());
|
||||
|
||||
Map<String, Cookie> cookies = (Map<String, Cookie>) headers.get("cookies");
|
||||
assertEquals(1, cookies.size());
|
||||
Cookie foo = cookies.get("foo");
|
||||
assertNotNull(foo);
|
||||
assertEquals(cookie, foo);
|
||||
}
|
||||
});
|
||||
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
request.addHeader("toLowerCase", true);
|
||||
|
||||
Object handler = this.handlerMapping.getHandler(request).getHandler();
|
||||
this.handlerAdapter.handle(request, response, handler);
|
||||
final String testResponse = response.getContentAsString();
|
||||
|
||||
@@ -354,9 +354,57 @@ By default the HTTP request will be generated using an instance of <classname>Si
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
<para>
|
||||
Since <emphasis>Spring Integration 3.0</emphasis>, in addition to the existing
|
||||
<code>#pathVariables</code> and <code>#requestParams</code> variables being available in payload and header
|
||||
expressions, other useful variables have been added.
|
||||
</para>
|
||||
<para>
|
||||
The entire list of available expression variables:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<emphasis>#requestParams</emphasis> - the <interfacename>MultiValueMap</interfacename> from the
|
||||
<interfacename>ServletRequest</interfacename> <code>parameterMap</code>.
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis>#pathVariables</emphasis> - the <interfacename>Map</interfacename> from URI Template placeholders and their values;
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis>#matrixVariables</emphasis> - the <interfacename>Map</interfacename> of <interfacename>MultiValueMap</interfacename>
|
||||
according to
|
||||
<ulink url="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-matrix-variables"
|
||||
>Spring MVC Specification</ulink>. Note, <emphasis>#matrixVariables</emphasis> require Spring MVC 3.2 or higher;
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis>#requestAttributes</emphasis> - the <interfacename>org.springframework.web.context.request.RequestAttributes</interfacename>
|
||||
associated with the current Request;
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis>#requestHeaders</emphasis> - the <classname>org.springframework.http.HttpHeaders</classname> object from the current Request;
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis>#cookies</emphasis> - the <interfacename>Map<String, Cookie></interfacename>
|
||||
of <classname>javax.servlet.http.Cookie</classname>s from the current Request.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
Note, all these values (and others) can be accessed within expressions in the downstream message
|
||||
flow via the <classname>ThreadLocal</classname> <interfacename>org.springframework.web.context.request.RequestAttributes</interfacename>
|
||||
variable, if that message flow is single-threaded and lives within the request thread:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int-:transformer
|
||||
expression="T(org.springframework.web.context.request.RequestContextHolder).
|
||||
requestAttributes.request.queryString"/>
|
||||
]]></programlisting>
|
||||
</para>
|
||||
|
||||
<para><emphasis>Outbound</emphasis></para>
|
||||
<para>
|
||||
To configure the outbound gateway you can use the namespace support as well. The following code snippet shows the different configuration options for an outbound Http gateway. Most importantly, notice that the 'http-method' and 'expected-response-type' are provided. Those are two of the most commonly configured values. The
|
||||
To configure the outbound gateway you can use the namespace support as well.
|
||||
The following code snippet shows the different configuration options for an outbound Http gateway.
|
||||
Most importantly, notice that the 'http-method' and 'expected-response-type' are provided. Those are two of the most commonly configured values. The
|
||||
default http-method is POST, and the default response type is <emphasis>null</emphasis>. With a null response type, the payload of the reply Message would
|
||||
contain the ResponseEntity as long as it's http status is a success (non-successful status codes will throw Exceptions).
|
||||
If you are expecting a different type, such as a <classname>String</classname>, then provide that fully-qualified class name as shown below.
|
||||
|
||||
@@ -333,12 +333,20 @@
|
||||
<interfacename>HttpMessageConverter</interfacename>s after the custom message converters.
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis role="bold">'If-(Un)Modified-Since' HTTP headers</emphasis> - previously,
|
||||
<emphasis role="bold">'If-(Un)Modified-Since' HTTP Headers</emphasis> - previously,
|
||||
'If-Modified-Since' and 'If-Unmodified-Since' HTTP headers were incorrectly processed
|
||||
within from/to HTTP headers mapping in the <classname>DefaultHttpHeaderMapper</classname>.
|
||||
Now, in addition correcting that issue, <classname>DefaultHttpHeaderMapper</classname> provides
|
||||
date parsing from formatted strings for any HTTP headers that accept date-time values.
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis role="bold">Inbound Endpoint Expression Variables</emphasis> -
|
||||
In addition to the existing <emphasis>#requestParams</emphasis> and <emphasis>#pathVariables</emphasis>,
|
||||
the <code><http:inbound-gateway/></code> and <code><http:inbound-channel-adapter/></code>
|
||||
now support additional useful variables: <emphasis>#matrixVariables</emphasis>, <emphasis>#requestAttributes</emphasis>,
|
||||
<emphasis>#requestHeaders</emphasis> and <emphasis>#cookies</emphasis>. These variables are available in
|
||||
both payload and header expressions.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
|
||||
Reference in New Issue
Block a user