INT-3463: Add encode-uri to ws:outbound-gateway
JIRA: https://jira.spring.io/browse/INT-3463
This commit is contained in:
committed by
Gary Russell
parent
d0fa88ac72
commit
e43671feb9
@@ -18,6 +18,7 @@ package org.springframework.integration.ws;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -25,6 +26,7 @@ import javax.xml.transform.TransformerException;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.integration.expression.ExpressionEvalMap;
|
||||
import org.springframework.integration.expression.ExpressionUtils;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -33,7 +35,8 @@ import org.springframework.messaging.MessageDeliveryException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.UriTemplate;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import org.springframework.ws.WebServiceMessage;
|
||||
import org.springframework.ws.WebServiceMessageFactory;
|
||||
import org.springframework.ws.client.core.FaultMessageResolver;
|
||||
@@ -59,7 +62,7 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro
|
||||
|
||||
private final WebServiceTemplate webServiceTemplate;
|
||||
|
||||
private final UriTemplate uriTemplate;
|
||||
private final String uri;
|
||||
|
||||
private final DestinationProvider destinationProvider;
|
||||
|
||||
@@ -71,23 +74,26 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro
|
||||
|
||||
private volatile boolean ignoreEmptyResponses = true;
|
||||
|
||||
private volatile boolean encodeUri = true;
|
||||
|
||||
protected volatile SoapHeaderMapper headerMapper = new DefaultSoapHeaderMapper();
|
||||
|
||||
public AbstractWebServiceOutboundGateway(final String uri, WebServiceMessageFactory messageFactory) {
|
||||
Assert.hasText(uri, "URI must not be empty");
|
||||
this.webServiceTemplate = new WebServiceTemplate(messageFactory);
|
||||
this.destinationProvider = null;
|
||||
this.uriTemplate = new UriTemplate(uri);
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public AbstractWebServiceOutboundGateway(DestinationProvider destinationProvider, WebServiceMessageFactory messageFactory) {
|
||||
public AbstractWebServiceOutboundGateway(DestinationProvider destinationProvider,
|
||||
WebServiceMessageFactory messageFactory) {
|
||||
Assert.notNull(destinationProvider, "DestinationProvider must not be null");
|
||||
this.webServiceTemplate = new WebServiceTemplate(messageFactory);
|
||||
this.destinationProvider = destinationProvider;
|
||||
// we always call WebServiceTemplate methods with an explicit URI argument,
|
||||
// but in case the WebServiceTemplate is accessed directly we'll set this:
|
||||
this.webServiceTemplate.setDestinationProvider(destinationProvider);
|
||||
this.uriTemplate = null;
|
||||
this.uri = null;
|
||||
}
|
||||
|
||||
public void setHeaderMapper(SoapHeaderMapper headerMapper) {
|
||||
@@ -97,7 +103,6 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro
|
||||
/**
|
||||
* Set the Map of URI variable expressions to evaluate against the outbound message
|
||||
* when replacing the variable placeholders in a URI template.
|
||||
*
|
||||
* @param uriVariableExpressions The URI variable expressions.
|
||||
*/
|
||||
public void setUriVariableExpressions(Map<String, Expression> uriVariableExpressions) {
|
||||
@@ -107,6 +112,17 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether the URI should be encoded after any <code>uriVariables</code>
|
||||
* are expanded and before sending the request. The default value is <code>true</code>.
|
||||
* @param encodeUri true if the URI should be encoded.
|
||||
* @see org.springframework.web.util.UriComponentsBuilder.
|
||||
* @since 4.1
|
||||
*/
|
||||
public void setEncodeUri(boolean encodeUri) {
|
||||
this.encodeUri = encodeUri;
|
||||
}
|
||||
|
||||
public void setReplyChannel(MessageChannel replyChannel) {
|
||||
this.setOutputChannel(replyChannel);
|
||||
}
|
||||
@@ -115,7 +131,6 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro
|
||||
* Specify whether empty String response payloads should be ignored.
|
||||
* The default is <code>true</code>. Set this to <code>false</code> if
|
||||
* you want to send empty String responses in reply Messages.
|
||||
*
|
||||
* @param ignoreEmptyResponses true if empty responses should be ignored.
|
||||
*/
|
||||
public void setIgnoreEmptyResponses(boolean ignoreEmptyResponses) {
|
||||
@@ -159,7 +174,13 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro
|
||||
|
||||
@Override
|
||||
public final Object handleRequestMessage(Message<?> requestMessage) {
|
||||
URI uri = this.prepareUri(requestMessage);
|
||||
URI uri = null;
|
||||
try {
|
||||
uri = this.prepareUri(requestMessage);
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
if (uri == null) {
|
||||
throw new MessageDeliveryException(requestMessage, "Failed to determine URI for " +
|
||||
"Web Service request in outbound gateway: " + this.getComponentName());
|
||||
@@ -175,22 +196,26 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract Object doHandle(String uri, Message<?> requestMessage, WebServiceMessageCallback requestCallback);
|
||||
|
||||
|
||||
private URI prepareUri(Message<?> requestMessage) {
|
||||
private URI prepareUri(Message<?> requestMessage) throws URISyntaxException {
|
||||
if (this.destinationProvider != null) {
|
||||
return this.destinationProvider.getDestination();
|
||||
}
|
||||
Map<String, Object> uriVariables = new HashMap<String, Object>();
|
||||
for (Map.Entry<String, Expression> entry : this.uriVariableExpressions.entrySet()) {
|
||||
Object value = entry.getValue().getValue(this.evaluationContext, requestMessage, String.class);
|
||||
uriVariables.put(entry.getKey(), value);
|
||||
}
|
||||
return this.uriTemplate.expand(uriVariables);
|
||||
|
||||
Map<String, Object> uriVariables = ExpressionEvalMap.from(this.uriVariableExpressions)
|
||||
.usingEvaluationContext(this.evaluationContext)
|
||||
.withRoot(requestMessage)
|
||||
.build();
|
||||
|
||||
UriComponents uriComponents = UriComponentsBuilder.fromUriString(uri).buildAndExpand(uriVariables);
|
||||
return this.encodeUri ? uriComponents.toUri() : new URI(uriComponents.toUriString());
|
||||
}
|
||||
|
||||
protected abstract class RequestMessageCallback extends TransformerObjectSupport implements WebServiceMessageCallback {
|
||||
|
||||
protected abstract Object doHandle(String uri, Message<?> requestMessage,
|
||||
WebServiceMessageCallback requestCallback);
|
||||
|
||||
protected abstract class RequestMessageCallback extends TransformerObjectSupport
|
||||
implements WebServiceMessageCallback {
|
||||
|
||||
private final WebServiceMessageCallback requestCallback;
|
||||
|
||||
@@ -206,8 +231,8 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro
|
||||
Object payload = this.requestMessage.getPayload();
|
||||
if (message instanceof SoapMessage){
|
||||
this.doWithMessageInternal(message, payload);
|
||||
AbstractWebServiceOutboundGateway.this.headerMapper.fromHeadersToRequest(this.requestMessage.getHeaders(),
|
||||
(SoapMessage) message);
|
||||
AbstractWebServiceOutboundGateway.this.headerMapper
|
||||
.fromHeadersToRequest(this.requestMessage.getHeaders(), (SoapMessage) message);
|
||||
if (this.requestCallback != null) {
|
||||
this.requestCallback.doWithMessage(message);
|
||||
}
|
||||
@@ -215,11 +240,13 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro
|
||||
|
||||
}
|
||||
|
||||
public abstract void doWithMessageInternal(WebServiceMessage message, Object payload) throws IOException, TransformerException;
|
||||
public abstract void doWithMessageInternal(WebServiceMessage message, Object payload)
|
||||
throws IOException, TransformerException;
|
||||
|
||||
}
|
||||
|
||||
protected abstract class ResponseMessageExtractor extends TransformerObjectSupport implements WebServiceMessageExtractor<Object> {
|
||||
protected abstract class ResponseMessageExtractor extends TransformerObjectSupport
|
||||
implements WebServiceMessageExtractor<Object> {
|
||||
|
||||
@Override
|
||||
public Object extractData(WebServiceMessage message)
|
||||
@@ -230,7 +257,10 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro
|
||||
if (resultObject != null && message instanceof SoapMessage){
|
||||
Map<String, Object> mappedMessageHeaders =
|
||||
AbstractWebServiceOutboundGateway.this.headerMapper.toHeadersFromReply((SoapMessage) message);
|
||||
return AbstractWebServiceOutboundGateway.this.getMessageBuilderFactory().withPayload(resultObject).copyHeaders(mappedMessageHeaders).build();
|
||||
return AbstractWebServiceOutboundGateway.this.getMessageBuilderFactory()
|
||||
.withPayload(resultObject)
|
||||
.copyHeaders(mappedMessageHeaders)
|
||||
.build();
|
||||
}
|
||||
else {
|
||||
return resultObject;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -85,6 +85,7 @@ public class WebServiceOutboundGatewayParser extends AbstractOutboundGatewayPars
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "reply-timeout", "sendTimeout");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "requires-reply");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "ignore-empty-responses");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "encode-uri");
|
||||
this.postProcessGateway(builder, element, parserContext);
|
||||
|
||||
IntegrationNamespaceUtils.configureHeaderMapper(element, builder, parserContext, DefaultSoapHeaderMapper.class, null);
|
||||
|
||||
@@ -108,6 +108,18 @@
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="encode-uri" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
When set to "false", the URI won't be encoded before the request is sent. This may be useful
|
||||
in some scenarios as it allows user control over the encoding, if needed. Default is "true".
|
||||
This attribute is ignored, if 'destination-provider' is specified.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:union memberTypes="xsd:boolean xsd:string" />
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="destination-provider" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
@@ -137,7 +137,7 @@ public class UriVariableTests {
|
||||
// expected
|
||||
assertThat(e.getCause(), Matchers.is(Matchers.instanceOf(WebServiceIOException.class))); // offline
|
||||
}
|
||||
assertEquals("http://localhost/spring-integration?param=test1%20%26%20test2", uri.get());
|
||||
assertEquals("http://localhost/spring-integration?param=test1%20&%20test2", uri.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -16,11 +16,9 @@
|
||||
|
||||
package org.springframework.integration.ws.config;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
@@ -30,25 +28,26 @@ import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.endpoint.PollingConsumer;
|
||||
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.ws.MarshallingWebServiceOutboundGateway;
|
||||
import org.springframework.integration.ws.SimpleWebServiceOutboundGateway;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.oxm.Marshaller;
|
||||
import org.springframework.oxm.Unmarshaller;
|
||||
import org.springframework.scheduling.support.PeriodicTrigger;
|
||||
import org.springframework.web.util.UriTemplate;
|
||||
import org.springframework.ws.WebServiceMessageFactory;
|
||||
import org.springframework.ws.client.core.FaultMessageResolver;
|
||||
import org.springframework.ws.client.core.SourceExtractor;
|
||||
import org.springframework.ws.client.core.WebServiceMessageCallback;
|
||||
import org.springframework.ws.client.core.WebServiceMessageExtractor;
|
||||
import org.springframework.ws.client.core.WebServiceTemplate;
|
||||
import org.springframework.ws.client.support.interceptor.ClientInterceptor;
|
||||
import org.springframework.ws.transport.WebServiceMessageSender;
|
||||
|
||||
@@ -368,7 +367,7 @@ public class WebServiceOutboundGatewayParserTests {
|
||||
assertEquals(SimpleWebServiceOutboundGateway.class, gateway.getClass());
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(gateway);
|
||||
assertEquals("Wrong DestinationProvider", stubProvider, accessor.getPropertyValue("destinationProvider"));
|
||||
assertNull(accessor.getPropertyValue("uriTemplate"));
|
||||
assertNull(accessor.getPropertyValue("uri"));
|
||||
Object destinationProviderObject = new DirectFieldAccessor(
|
||||
accessor.getPropertyValue("webServiceTemplate")).getPropertyValue("destinationProvider");
|
||||
assertEquals("Wrong DestinationProvider", stubProvider,destinationProviderObject);
|
||||
@@ -404,8 +403,23 @@ public class WebServiceOutboundGatewayParserTests {
|
||||
assertEquals(EventDrivenConsumer.class, endpoint.getClass());
|
||||
MessageHandler handler = TestUtils.getPropertyValue(endpoint, "handler", MessageHandler.class);
|
||||
assertNull(TestUtils.getPropertyValue(handler, "destinationProvider"));
|
||||
UriTemplate uriTemplate = TestUtils.getPropertyValue(handler, "uriTemplate", UriTemplate.class);
|
||||
assertEquals(URI.create("jms:wsQueue"), uriTemplate.expand());
|
||||
assertFalse(TestUtils.getPropertyValue(handler, "encodeUri", Boolean.class));
|
||||
|
||||
WebServiceTemplate webServiceTemplate = TestUtils.getPropertyValue(handler, "webServiceTemplate",
|
||||
WebServiceTemplate.class);
|
||||
webServiceTemplate = spy(webServiceTemplate);
|
||||
|
||||
doReturn(null).when(webServiceTemplate).sendAndReceive(anyString(),
|
||||
any(WebServiceMessageCallback.class),
|
||||
any(WebServiceMessageExtractor.class));
|
||||
|
||||
new DirectFieldAccessor(handler).setPropertyValue("webServiceTemplate", webServiceTemplate);
|
||||
|
||||
handler.handleMessage(new GenericMessage<String>("foo"));
|
||||
|
||||
verify(webServiceTemplate).sendAndReceive(eq("jms:wsQueue"),
|
||||
any(WebServiceMessageCallback.class),
|
||||
any(WebServiceMessageExtractor.class));
|
||||
}
|
||||
|
||||
@Test(expected = BeanDefinitionParsingException.class)
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
|
||||
<ws:outbound-gateway id="gatewayWithJmsUri"
|
||||
request-channel="inputChannel"
|
||||
encode-uri="false"
|
||||
uri="jms:wsQueue" />
|
||||
|
||||
<bean id="sourceExtractor" class="org.springframework.integration.ws.config.StubSourceExtractor"/>
|
||||
|
||||
@@ -38,5 +38,13 @@
|
||||
See <xref linkend="sms-caution"/> for more information.
|
||||
</para>
|
||||
</section>
|
||||
<section id="4.1-ws-encode-uri">
|
||||
<title>Web Service Outbound Gateway: encode-uri</title>
|
||||
<para>
|
||||
The <code><ws:outbound-gateway/></code> now
|
||||
provides an <code>encode-uri</code> attribute to allow disabling the encoding of the URI object
|
||||
before sending the request.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
@@ -154,5 +154,26 @@ as per standard Spring Web Services configuration.
|
||||
If a <classname>DestinationProvider</classname> is supplied, variable substitution is not supported
|
||||
and a configuration error will result if variables are provided.
|
||||
</para>
|
||||
<para>
|
||||
<emphasis>Controlling URI Encoding</emphasis>
|
||||
</para>
|
||||
<para>
|
||||
By default, the URL string is encoded (see
|
||||
<ulink url="http://static.springsource.org/spring/docs/current/javadoc-api/org/springframework/web/util/UriComponentsBuilder.html">UriComponentsBuilder</ulink>)
|
||||
to the URI object before sending the request. In some scenarios with a non-standard URI it is
|
||||
undesirable to perform the encoding. Since <emphasis>version 4.1 </emphasis> the
|
||||
<code><ws:outbound-gateway/></code> provides an <code>encode-uri</code> attribute.
|
||||
To disable encoding the URL, this attribute should be set to <code>false</code> (by default it is <code>true</code>).
|
||||
If you wish to partially encode some of the URL, this can be achieved using an <code>expression</code> within a
|
||||
<code><uri-variable/></code>:
|
||||
<programlisting
|
||||
language="xml"><![CDATA[<ws:outbound-gateway url="http://somehost/%2f/fooApps?bar={param}" encode-uri="false">
|
||||
<http:uri-variable name="param"
|
||||
expression="T(org.apache.commons.httpclient.util.URIUtil)
|
||||
.encodeWithinQuery('Hellow World!')"/>
|
||||
</ws:outbound-gateway>]]></programlisting>
|
||||
Note, <code>encode-uri</code> is ignored, if <classname>DestinationProvider</classname> is supplied.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
Reference in New Issue
Block a user