From 9ce8c7b519e18790327244e14906e25c1fd4cf0f Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Thu, 22 Sep 2011 08:17:23 -0400 Subject: [PATCH] add URI variable Expressions to outbound gateway parser and schema now support 'uri-variable' add test for 'uri-variable' sub-elements updated pom --- build.gradle | 3 +- spring-integration-ws/pom.xml | 38 +++--- .../ws/AbstractWebServiceOutboundGateway.java | 129 +++++++++++++++++- .../MarshallingWebServiceOutboundGateway.java | 62 +++++++-- .../ws/SimpleWebServiceOutboundGateway.java | 23 +++- .../config/FixedUriDestinationProvider.java | 54 -------- .../WebServiceOutboundGatewayParser.java | 59 ++++---- .../ws/config/spring-integration-ws-2.1.xsd | 39 +++++- .../ws/config/UriVariableTests-context.xml | 17 +++ .../ws/config/UriVariableTests.java | 103 ++++++++++++++ .../WebServiceOutboundGatewayParserTests.java | 17 ++- 11 files changed, 411 insertions(+), 133 deletions(-) delete mode 100644 spring-integration-ws/src/main/java/org/springframework/integration/ws/config/FixedUriDestinationProvider.java create mode 100644 spring-integration-ws/src/test/java/org/springframework/integration/ws/config/UriVariableTests-context.xml create mode 100644 spring-integration-ws/src/test/java/org/springframework/integration/ws/config/UriVariableTests.java diff --git a/build.gradle b/build.gradle index 5db8e69cb8..060323abdc 100644 --- a/build.gradle +++ b/build.gradle @@ -471,7 +471,8 @@ project('spring-integration-ws') { compile project(":spring-integration-core") compile "org.springframework:spring-expression:$springVersion" compile "org.springframework:spring-oxm:$springVersion" - compile ("org.springframework.ws:spring-ws-core:$springWsVersion"){ + compile "org.springframework:spring-web:$springVersion" + compile ("org.springframework.ws:spring-ws-core:$springWsVersion") { exclude group: 'org.springframework', module: 'spring-webmvc' exclude group: 'org.springframework', module: 'spring-web' exclude group: 'org.springframework', module: 'spring-context-support' diff --git a/spring-integration-ws/pom.xml b/spring-integration-ws/pom.xml index eecf4fffcd..70ded5de1c 100644 --- a/spring-integration-ws/pom.xml +++ b/spring-integration-ws/pom.xml @@ -88,12 +88,6 @@ - - org.springframework.integration - spring-integration-test - 2.1.0.BUILD-SNAPSHOT - test - com.sun.xml.messaging.saaj saaj-impl @@ -101,6 +95,12 @@ compile true + + org.springframework.integration + spring-integration-test + 2.1.0.BUILD-SNAPSHOT + test + org.springframework spring-test @@ -119,6 +119,12 @@ 1.2.12 test + + cglib + cglib-nodep + 2.2 + test + org.springframework.ws spring-ws-core @@ -140,10 +146,10 @@ - cglib - cglib-nodep - 2.2 - test + org.springframework + spring-web + 3.0.6.RELEASE + compile javax.xml.soap @@ -189,12 +195,6 @@ compile true - - org.springframework.integration - spring-integration-core - 2.1.0.BUILD-SNAPSHOT - compile - org.hamcrest hamcrest-all @@ -207,6 +207,12 @@ 1.8.4 test + + org.springframework.integration + spring-integration-core + 2.1.0.BUILD-SNAPSHOT + compile + junit junit-dep diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java index 66187f400c..6d414b3a2b 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceOutboundGateway.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. @@ -17,13 +17,31 @@ package org.springframework.integration.ws; import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.context.expression.MapAccessor; +import org.springframework.core.convert.ConversionService; +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.integration.Message; import org.springframework.integration.MessageChannel; +import org.springframework.integration.MessageDeliveryException; import org.springframework.integration.MessagingException; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; 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.UriUtils; import org.springframework.ws.WebServiceMessage; import org.springframework.ws.WebServiceMessageFactory; import org.springframework.ws.client.core.FaultMessageResolver; @@ -43,20 +61,58 @@ import org.springframework.ws.transport.WebServiceMessageSender; */ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyProducingMessageHandler { + private static final ExpressionParser PARSER = new SpelExpressionParser(); + + private final WebServiceTemplate webServiceTemplate; + private final UriTemplate uriTemplate; + + private final DestinationProvider destinationProvider; + + private final Map uriVariableExpressions = new HashMap(); + + private final StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); + private volatile WebServiceMessageCallback requestCallback; private volatile boolean ignoreEmptyResponses = true; - public AbstractWebServiceOutboundGateway(DestinationProvider destinationProvider, WebServiceMessageFactory messageFactory) { - Assert.notNull(destinationProvider, "DestinationProvider must not be null"); - this.webServiceTemplate = (messageFactory != null) ? + public AbstractWebServiceOutboundGateway(String uri, WebServiceMessageFactory messageFactory) { + Assert.hasText(uri, "URI must not be empty"); + this.webServiceTemplate = (messageFactory != null) ? new WebServiceTemplate(messageFactory) : new WebServiceTemplate(); - this.webServiceTemplate.setDestinationProvider(destinationProvider); + this.destinationProvider = null; + this.uriTemplate = new HttpUrlTemplate(uri); } + public AbstractWebServiceOutboundGateway(DestinationProvider destinationProvider, WebServiceMessageFactory messageFactory) { + Assert.notNull(destinationProvider, "DestinationProvider must not be null"); + this.webServiceTemplate = (messageFactory != null) ? + new WebServiceTemplate(messageFactory) : new WebServiceTemplate(); + 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; + } + + + /** + * Set the Map of URI variable expressions to evaluate against the outbound message + * when replacing the variable placeholders in a URI template. + */ + public void setUriVariableExpressions(Map uriVariableExpressions) { + synchronized (this.uriVariableExpressions) { + this.uriVariableExpressions.clear(); + if (!CollectionUtils.isEmpty(uriVariableExpressions)) { + for (Map.Entry entry : uriVariableExpressions.entrySet()) { + this.uriVariableExpressions.put(entry.getKey(), PARSER.parseExpression(entry.getValue())); + } + } + } + } public void setReplyChannel(MessageChannel replyChannel) { this.setOutputChannel(replyChannel); @@ -95,13 +151,32 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro this.webServiceTemplate.setInterceptors(interceptors); } + @Override + public void onInit() { + super.onInit(); + BeanFactory beanFactory = this.getBeanFactory(); + if (beanFactory != null) { + this.evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory)); + } + ConversionService conversionService = this.getConversionService(); + if (conversionService != null) { + this.evaluationContext.setTypeConverter(new StandardTypeConverter(conversionService)); + } + this.evaluationContext.addPropertyAccessor(new MapAccessor()); + } + protected WebServiceTemplate getWebServiceTemplate() { return this.webServiceTemplate; } @Override public final Object handleRequestMessage(Message message) { - Object responsePayload = this.doHandle(message.getPayload(), this.getRequestCallback(message)); + URI uri = prepareUri(message); + if (uri == null) { + throw new MessageDeliveryException(message, "Failed to determine URI for " + + "Web Service request in outbound gateway: " + this.getComponentName()); + } + Object responsePayload = this.doHandle(uri.toString(), message.getPayload(), this.getRequestCallback(message)); if (responsePayload != null) { boolean shouldIgnore = (this.ignoreEmptyResponses && responsePayload instanceof String && !StringUtils.hasText((String) responsePayload)); @@ -112,9 +187,21 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro return null; } - protected abstract Object doHandle(Object requestPayload, WebServiceMessageCallback requestCallback); + protected abstract Object doHandle(String uri, Object requestPayload, WebServiceMessageCallback requestCallback); + private URI prepareUri(Message requestMessage) { + if (this.destinationProvider != null) { + return this.destinationProvider.getDestination(); + } + Map uriVariables = new HashMap(); + for (Map.Entry entry : this.uriVariableExpressions.entrySet()) { + Object value = entry.getValue().getValue(this.evaluationContext, requestMessage, String.class); + uriVariables.put(entry.getKey(), value); + } + return this.uriTemplate.expand(uriVariables); + } + private WebServiceMessageCallback getRequestCallback(Message requestMessage) { String soapAction = requestMessage.getHeaders().get(WebServiceHeaders.SOAP_ACTION, String.class); return (soapAction != null) ? @@ -147,4 +234,32 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro } } + + /** + * HTTP-specific subclass of UriTemplate, overriding the encode method. + * This was copied from RestTemplate in version 3.0.6 (since it's private) + */ + @SuppressWarnings("serial") + private static class HttpUrlTemplate extends UriTemplate { + + public HttpUrlTemplate(String uriTemplate) { + super(uriTemplate); + } + + @Override + protected URI encodeUri(String uri) { + try { + String encoded = UriUtils.encodeHttpUrl(uri, "UTF-8"); + return new URI(encoded); + } + catch (UnsupportedEncodingException ex) { + // should not happen, UTF-8 is always supported + throw new IllegalStateException(ex); + } + catch (URISyntaxException ex) { + throw new IllegalArgumentException("Could not create HTTP URL from [" + uri + "]: " + ex, ex); + } + } + } + } diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java index f87ce6769c..3773faee64 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceOutboundGateway.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 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. @@ -35,10 +35,7 @@ public class MarshallingWebServiceOutboundGateway extends AbstractWebServiceOutb public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvider, Marshaller marshaller, Unmarshaller unmarshaller, WebServiceMessageFactory messageFactory) { super(destinationProvider, messageFactory); - Assert.notNull(marshaller, "marshaller must not be null"); - Assert.notNull(unmarshaller, "unmarshaller must not be null"); - this.getWebServiceTemplate().setMarshaller(marshaller); - this.getWebServiceTemplate().setUnmarshaller(unmarshaller); + this.configureMarshallers(marshaller, unmarshaller); } public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvider, Marshaller marshaller, Unmarshaller unmarshaller) { @@ -47,23 +44,60 @@ public class MarshallingWebServiceOutboundGateway extends AbstractWebServiceOutb public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvider, Marshaller marshaller, WebServiceMessageFactory messageFactory) { super(destinationProvider, messageFactory); - Assert.notNull(marshaller, "marshaller must not be null"); - Assert.isInstanceOf(Unmarshaller.class, marshaller, - "Marshaller [" + marshaller + "] does not implement the Unmarshaller interface. " + - "Please set an Unmarshaller explicitly by using the " + this.getClass().getName() + - "(DestinationProvider destinationProvider, Marshaller marshaller, Unmarshaller unmarshaller) constructor."); - this.getWebServiceTemplate().setMarshaller(marshaller); - this.getWebServiceTemplate().setUnmarshaller((Unmarshaller) marshaller); + this.configureMarshallers(marshaller); } public MarshallingWebServiceOutboundGateway(DestinationProvider destinationProvider, Marshaller marshaller) { this(destinationProvider, marshaller, (WebServiceMessageFactory) null); } + public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller, Unmarshaller unmarshaller, WebServiceMessageFactory messageFactory) { + super(uri, messageFactory); + this.configureMarshallers(marshaller, unmarshaller); + } + + public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller, Unmarshaller unmarshaller) { + this(uri, marshaller, unmarshaller, null); + } + + public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller, WebServiceMessageFactory messageFactory) { + super(uri, messageFactory); + this.configureMarshallers(marshaller); + } + + public MarshallingWebServiceOutboundGateway(String uri, Marshaller marshaller) { + this(uri, marshaller, (WebServiceMessageFactory) null); + } + + + /** + * Sets the provided Marshaller and Unmarshaller on this gateway's WebServiceTemplate. + * Neither may be null. + */ + private void configureMarshallers(Marshaller marshaller, Unmarshaller unmarshaller) { + Assert.notNull(marshaller, "marshaller must not be null"); + Assert.notNull(unmarshaller, "unmarshaller must not be null"); + this.getWebServiceTemplate().setMarshaller(marshaller); + this.getWebServiceTemplate().setUnmarshaller(unmarshaller); + } + + /** + * Sets the provided Marshaller on this gateway's WebServiceTemplate as both its + * Marshaller and Unmarshaller. Therefore, it must implement both, and it must not be null. + */ + private void configureMarshallers(Marshaller marshaller) { + Assert.notNull(marshaller, "marshaller must not be null"); + Assert.isInstanceOf(Unmarshaller.class, marshaller, + "Marshaller [" + marshaller + "] does not implement the Unmarshaller interface. " + + "Please set an Unmarshaller explicitly by using one of the constructors that accepts " + + "both Marshaller and Unmarshaller arguments."); + this.getWebServiceTemplate().setMarshaller(marshaller); + this.getWebServiceTemplate().setUnmarshaller((Unmarshaller) marshaller); + } @Override - protected Object doHandle(Object requestPayload, WebServiceMessageCallback requestCallback) { - return this.getWebServiceTemplate().marshalSendAndReceive(requestPayload, requestCallback); + protected Object doHandle(String uri, Object requestPayload, WebServiceMessageCallback requestCallback) { + return this.getWebServiceTemplate().marshalSendAndReceive(uri, requestPayload, requestCallback); } } diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java index 958aa7c336..6d71b08edb 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceOutboundGateway.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. @@ -58,23 +58,36 @@ public class SimpleWebServiceOutboundGateway extends AbstractWebServiceOutboundG this.sourceExtractor = (sourceExtractor != null) ? sourceExtractor : new DefaultSourceExtractor(); } + public SimpleWebServiceOutboundGateway(String uri) { + this(uri, null, null); + } + + public SimpleWebServiceOutboundGateway(String uri, SourceExtractor sourceExtractor) { + this(uri, sourceExtractor, (WebServiceMessageFactory) null); + } + + public SimpleWebServiceOutboundGateway(String uri, SourceExtractor sourceExtractor, WebServiceMessageFactory messageFactory) { + super(uri, messageFactory); + this.sourceExtractor = (sourceExtractor != null) ? sourceExtractor : new DefaultSourceExtractor(); + } + @Override - protected Object doHandle(Object requestPayload, WebServiceMessageCallback requestCallback) { + protected Object doHandle(String uri, Object requestPayload, WebServiceMessageCallback requestCallback) { if (requestPayload instanceof Source) { return this.getWebServiceTemplate().sendSourceAndReceive( - (Source) requestPayload, requestCallback, this.sourceExtractor); + uri, (Source) requestPayload, requestCallback, this.sourceExtractor); } if (requestPayload instanceof String) { StringResult result = new StringResult(); this.getWebServiceTemplate().sendSourceAndReceiveToResult( - new StringSource((String) requestPayload), requestCallback, result); + uri, new StringSource((String) requestPayload), requestCallback, result); return result.toString(); } if (requestPayload instanceof Document) { DOMResult result = new DOMResult(); this.getWebServiceTemplate().sendSourceAndReceiveToResult( - new DOMSource((Document) requestPayload), requestCallback, result); + uri, new DOMSource((Document) requestPayload), requestCallback, result); return result.getNode(); } throw new MessagingException("Unsupported payload type '" + requestPayload.getClass() + diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/FixedUriDestinationProvider.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/FixedUriDestinationProvider.java deleted file mode 100644 index 019a6fd652..0000000000 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/FixedUriDestinationProvider.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2002-2009 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.ws.config; - -import java.net.URI; - -/** - * @author Jonas Partner - * @author Mark Fisher - * @since 1.0.3 - */ -import org.springframework.util.Assert; -import org.springframework.ws.client.support.destination.DestinationProvider; - - -/** - * A {@link DestinationProvider} implementation that returns a fixed URI. - * This is used by the outbound gateway parser when no 'destination-provider' - * reference has been configured. - * - * @author Jonas Partner - * @author Mark Fisher - * @since 1.0.3 - */ -class FixedUriDestinationProvider implements DestinationProvider { - - private final URI uri; - - - public FixedUriDestinationProvider(String uri) { - Assert.hasText(uri, "uri must not be null or empty"); - this.uri = URI.create(uri); - } - - - public URI getDestination() { - return this.uri; - } - -} diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParser.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParser.java index 9936c764f6..717659e5e7 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParser.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParser.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. @@ -16,17 +16,21 @@ package org.springframework.integration.ws.config; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.w3c.dom.Element; -import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedList; -import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.xml.AbstractOutboundGatewayParser; import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import org.springframework.util.xml.DomUtils; /** * Parser for the <outbound-gateway/> element in the 'ws' namespace. @@ -46,34 +50,41 @@ public class WebServiceOutboundGatewayParser extends AbstractOutboundGatewayPars return BASE_PACKAGE + "." + simpleClassName; } - @Override + @Override protected BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) { - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(this.getGatewayClassName(element)); - this.buildDestinationProvider(builder, element, parserContext); + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(this.getGatewayClassName(element)); + String uri = element.getAttribute("uri"); + String destinationProvider = element.getAttribute("destination-provider"); + List uriVariableElements = DomUtils.getChildElementsByTagName(element, "uri-variable"); + if (!(StringUtils.hasText(destinationProvider) ^ StringUtils.hasText(uri))) { + parserContext.getReaderContext().error( + "Exactly one of 'uri' or 'destination-provider' is required.", element); + } + if (StringUtils.hasText(destinationProvider)) { + if (!CollectionUtils.isEmpty(uriVariableElements)) { + parserContext.getReaderContext().error("No 'uri-variable' sub-elements are allowed when " + + "a 'destination-provider' reference has been provided.", element); + } + builder.addConstructorArgReference(destinationProvider); + } + else { + builder.addConstructorArgValue(uri); + if (!CollectionUtils.isEmpty(uriVariableElements)) { + Map uriVariableExpressions = new HashMap(); + for (Element uriVariableElement : uriVariableElements) { + String name = uriVariableElement.getAttribute("name"); + String expression = uriVariableElement.getAttribute("expression"); + uriVariableExpressions.put(name, expression); + } + builder.addPropertyValue("uriVariableExpressions", uriVariableExpressions); + } + } IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel"); IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "ignore-empty-responses"); this.postProcessGateway(builder, element, parserContext); return builder; } - private void buildDestinationProvider(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) { - String uri = element.getAttribute("uri"); - String destinationProvider = element.getAttribute("destination-provider"); - if (!(StringUtils.hasText(destinationProvider) ^ StringUtils.hasText(uri))) { - parserContext.getReaderContext().error("Exactly one of 'uri' or 'destination-provider' is required.", element); - return; - } - if (StringUtils.hasText(destinationProvider)) { - builder.addConstructorArgReference(destinationProvider); - } - else { - ConstructorArgumentValues cavs = new ConstructorArgumentValues(); - cavs.addGenericArgumentValue(uri); - builder.addConstructorArgValue(new RootBeanDefinition( - BASE_PACKAGE +".config.FixedUriDestinationProvider", cavs, null)); - } - } - @Override protected void postProcessGateway(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) { String marshallerRef = element.getAttribute("marshaller"); diff --git a/spring-integration-ws/src/main/resources/org/springframework/integration/ws/config/spring-integration-ws-2.1.xsd b/spring-integration-ws/src/main/resources/org/springframework/integration/ws/config/spring-integration-ws-2.1.xsd index e2dc8ff85d..bac94edf91 100644 --- a/spring-integration-ws/src/main/resources/org/springframework/integration/ws/config/spring-integration-ws-2.1.xsd +++ b/spring-integration-ws/src/main/resources/org/springframework/integration/ws/config/spring-integration-ws-2.1.xsd @@ -26,9 +26,10 @@ Defines a Web Service based outbound Messaging Gateway. - + - + + @@ -64,10 +65,13 @@ - + + (e.g. registry lookup), then configure a 'destination-provider' reference instead. Aternatively, + this URI may include {placeholders} whose values are determined by evaluating SpEL expressions + provided via 'uri-variable' sub-elements. The root object for those evaluations is the actual + request Message at runtime, i.e. you can access its payload or headers in the expression. + ]]> @@ -203,6 +207,31 @@ + + + + + + + + + + + + + + + + diff --git a/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/UriVariableTests-context.xml b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/UriVariableTests-context.xml new file mode 100644 index 0000000000..38b7de9f5e --- /dev/null +++ b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/UriVariableTests-context.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/UriVariableTests.java b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/UriVariableTests.java new file mode 100644 index 0000000000..c3093646d0 --- /dev/null +++ b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/UriVariableTests.java @@ -0,0 +1,103 @@ +/* + * 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.ws.config; + +import static org.junit.Assert.assertEquals; + +import java.net.URI; +import java.net.URISyntaxException; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.MessageChannel; +import org.springframework.integration.MessageHandlingException; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.ws.client.WebServiceClientException; +import org.springframework.ws.client.WebServiceIOException; +import org.springframework.ws.client.support.interceptor.ClientInterceptor; +import org.springframework.ws.context.MessageContext; +import org.springframework.ws.transport.context.TransportContext; +import org.springframework.ws.transport.context.TransportContextHolder; +import org.springframework.ws.transport.http.HttpUrlConnection; + +/** + * @author Mark Fisher + * @since 2.1 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +public class UriVariableTests { + + @Autowired + private ApplicationContext context; + + @Test + public void checkUriVariables() { + MessageChannel input = context.getBean("input", MessageChannel.class); + TestClientInterceptor interceptor = context.getBean("interceptor", TestClientInterceptor.class); + Message message = MessageBuilder.withPayload("").setHeader("bar", "BAR").build(); + try { + input.send(message); + } + catch (MessageHandlingException e) { + // expected + assertEquals(WebServiceIOException.class, e.getCause().getClass()); + } + assertEquals("http://localhost/test/FOO/BAR", interceptor.getLastUri().toString()); + } + + + private static class TestClientInterceptor implements ClientInterceptor { + + private URI lastUri; + + private URI getLastUri() { + return this.lastUri; + } + + public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException { + TransportContext tc = TransportContextHolder.getTransportContext(); + if (tc != null && tc.getConnection() instanceof HttpUrlConnection) { + try { + this.lastUri = ((HttpUrlConnection) tc.getConnection()).getUri(); + } + catch (URISyntaxException e) { + throw new IllegalStateException(e); + } + } + else { + throw new IllegalStateException("expected HttpUrlConnection as TransportContext"); + } + return false; + } + + public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException { + return false; + } + + public boolean handleFault(MessageContext messageContext) throws WebServiceClientException { + return false; + } + } + +} diff --git a/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParserTests.java b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParserTests.java index 1dc24215b7..4c845dcfdc 100644 --- a/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParserTests.java +++ b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParserTests.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. @@ -17,6 +17,7 @@ package org.springframework.integration.ws.config; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import org.junit.Test; @@ -337,20 +338,22 @@ public class WebServiceOutboundGatewayParserTests { assertEquals(messageFactory, templateAccessor.getPropertyValue("messageFactory")); } - @Test + @Test public void simpleGatewayWithDestinationProvider() { ApplicationContext context = new ClassPathXmlApplicationContext( "simpleWebServiceOutboundGatewayParserTests.xml", this.getClass()); AbstractEndpoint endpoint = (AbstractEndpoint) context.getBean("gatewayWithDestinationProvider"); assertEquals(EventDrivenConsumer.class, endpoint.getClass()); Object gateway = new DirectFieldAccessor(endpoint).getPropertyValue("handler"); + StubDestinationProvider stubProvider = (StubDestinationProvider) context.getBean("destinationProvider"); assertEquals(SimpleWebServiceOutboundGateway.class, gateway.getClass()); DirectFieldAccessor accessor = new DirectFieldAccessor(gateway); - Object destinationProviderObject = new DirectFieldAccessor( - accessor.getPropertyValue("webServiceTemplate")).getPropertyValue("destinationProvider"); - StubDestinationProvider stubProvider = (StubDestinationProvider)context.getBean("destinationProvider"); - assertEquals("Wrong DestinationProvider", stubProvider, destinationProviderObject ); - } + assertEquals("Wrong DestinationProvider", stubProvider, accessor.getPropertyValue("destinationProvider")); + assertNull(accessor.getPropertyValue("uriTemplate")); + Object destinationProviderObject = new DirectFieldAccessor( + accessor.getPropertyValue("webServiceTemplate")).getPropertyValue("destinationProvider"); + assertEquals("Wrong DestinationProvider", stubProvider,destinationProviderObject); + } @Test(expected = BeanDefinitionParsingException.class) public void invalidGatewayWithBothUriAndDestinationProvider() {