From 6b7ae6ceedd85d5922a2cd010b80f1d36b9c1789 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Mon, 27 Aug 2012 17:59:06 +0300 Subject: [PATCH] INT-2720: UriTemplate Support for S-WS Transports Remove deprecation in the WS module JIRA: https://jira.springsource.org/browse/INT-2720 https://jira.springsource.org/browse/INT-2530 INT-2720: Polishing after rebase INT-2720: Add a note to the Reference Manual Polishing to avoid extra whitespace before name INT-2720: Pr comments & polishing * Revert removed public API * Revert tabs in What's new * Change real URL to fake to allow for tests to work off-line * Add smackx dependency to avoid error messages in logs --- build.gradle | 10 + .../ws/AbstractWebServiceOutboundGateway.java | 106 +++------- .../WebServiceOutboundGatewayParser.java | 30 +-- .../SimpleWebServiceOutboundGatewayTests.java | 6 +- .../ws/config/UriVariableTests-context.xml | 75 ++++++- .../ws/config/UriVariableTests.java | 184 ++++++++++++++++-- .../WebServiceOutboundGatewayParserTests.java | 9 +- .../src/test/resources/.svnignore | 0 src/reference/docbook/index.xml | 33 ++-- src/reference/docbook/whats-new.xml | 8 +- src/reference/docbook/ws.xml | 37 ++-- 11 files changed, 332 insertions(+), 166 deletions(-) delete mode 100644 spring-integration-ws/src/test/resources/.svnignore diff --git a/build.gradle b/build.gradle index c296dead46..e8ac7a29cc 100644 --- a/build.gradle +++ b/build.gradle @@ -535,6 +535,16 @@ project('spring-integration-ws') { testCompile project(":spring-integration-test") testCompile "stax:stax-api:1.0.1" testCompile "xstream:xstream:1.2.2" + testCompile ("org.springframework.ws:spring-ws-support:$springWsVersion") { + exclude group: 'org.springframework' + } + testCompile ("org.springframework:spring-jms:$springVersion") { + exclude group: 'org.springframework' + } + testCompile "org.apache.geronimo.specs:geronimo-jms_1.1_spec:1.1" + testCompile "org.igniterealtime.smack:smack:3.2.1" + testCompile "org.igniterealtime.smack:smackx:3.2.1" + testCompile "javax.mail:mail:1.4.5" } // suppress saaj path warnings 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 fcc5f07eaf..3320ed1bff 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-2012 the original author or authors. + * Copyright 2002-2013 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,31 +17,24 @@ 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 javax.xml.transform.TransformerException; -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.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.expression.ExpressionUtils; import org.springframework.integration.handler.AbstractReplyProducingMessageHandler; import org.springframework.integration.support.MessageBuilder; 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; @@ -61,6 +54,7 @@ import org.springframework.xml.transform.TransformerObjectSupport; * @author Jonas Partner * @author Oleg Zhurakousky * @author Gary Russell + * @author Artem Bilan */ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyProducingMessageHandler { @@ -72,7 +66,7 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro private final Map uriVariableExpressions = new HashMap(); - private final StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); + private volatile StandardEvaluationContext evaluationContext; private volatile WebServiceMessageCallback requestCallback; @@ -82,30 +76,14 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro public AbstractWebServiceOutboundGateway(final String uri, WebServiceMessageFactory messageFactory) { Assert.hasText(uri, "URI must not be empty"); - this.webServiceTemplate = (messageFactory != null) ? - new WebServiceTemplate(messageFactory) : new WebServiceTemplate(); - if (uri.toLowerCase().startsWith("http")) { - this.uriTemplate = new HttpUrlTemplate(uri); - this.destinationProvider = null; - } - else { - this.uriTemplate = null; - this.destinationProvider = new DestinationProvider() { - private volatile URI cachedUri; - public URI getDestination() { - if (this.cachedUri == null) { - this.cachedUri = URI.create(uri); - } - return this.cachedUri; - } - }; - } + this.webServiceTemplate = new WebServiceTemplate(messageFactory); + this.destinationProvider = null; + this.uriTemplate = new UriTemplate(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.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: @@ -157,29 +135,25 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro this.webServiceTemplate.setMessageSender(messageSender); } - public void setMessageSenders(WebServiceMessageSender[] messageSenders) { + public void setMessageSenders(WebServiceMessageSender... messageSenders) { this.webServiceTemplate.setMessageSenders(messageSenders); } - public void setInterceptors(ClientInterceptor[] interceptors) { + public void setInterceptors(ClientInterceptor... interceptors) { this.webServiceTemplate.setInterceptors(interceptors); } @Override public void onInit() { super.onInit(); - BeanFactory beanFactory = this.getBeanFactory(); - if (beanFactory != null) { - this.evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory)); + if (this.getBeanFactory() != null) { + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory()); } - ConversionService conversionService = this.getConversionService(); - if (conversionService != null) { - this.evaluationContext.setTypeConverter(new StandardTypeConverter(conversionService)); + else { + this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(); } - this.evaluationContext.addPropertyAccessor(new MapAccessor()); - Assert.state(this.destinationProvider != null ? CollectionUtils.isEmpty(this.uriVariableExpressions) : true, - "uri variables are not supported when a DestinationProvider is supplied, or the uri " + - "scheme is not http: or https:"); + Assert.state(this.destinationProvider == null || CollectionUtils.isEmpty(this.uriVariableExpressions), + "uri variables are not supported when a DestinationProvider is supplied."); } protected WebServiceTemplate getWebServiceTemplate() { @@ -188,7 +162,7 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro @Override public final Object handleRequestMessage(Message requestMessage) { - URI uri = prepareUri(requestMessage); + URI uri = this.prepareUri(requestMessage); if (uri == null) { throw new MessageDeliveryException(requestMessage, "Failed to determine URI for " + "Web Service request in outbound gateway: " + this.getComponentName()); @@ -222,6 +196,7 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro protected abstract class RequestMessageCallback extends TransformerObjectSupport implements WebServiceMessageCallback { private final WebServiceMessageCallback requestCallback; + private final Message requestMessage; public RequestMessageCallback(WebServiceMessageCallback requestCallback, Message requestMessage){ @@ -233,15 +208,17 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro Object payload = this.requestMessage.getPayload(); if (message instanceof SoapMessage){ this.doWithMessageInternal(message, payload); - headerMapper.fromHeadersToRequest(this.requestMessage.getHeaders(), (SoapMessage)message); - if (requestCallback != null) { - requestCallback.doWithMessage(message); - } + AbstractWebServiceOutboundGateway.this.headerMapper.fromHeadersToRequest(this.requestMessage.getHeaders(), + (SoapMessage) message); + if (this.requestCallback != null) { + this.requestCallback.doWithMessage(message); + } } } public abstract void doWithMessageInternal(WebServiceMessage message, Object payload) throws IOException, TransformerException; + } protected abstract class ResponseMessageExtractor extends TransformerObjectSupport implements WebServiceMessageExtractor { @@ -251,10 +228,10 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro Object resultObject = this.doExtractData(message); - if (message instanceof SoapMessage){ - Map mappedMessageHeaders = headerMapper.toHeadersFromReply((SoapMessage) message); - Message siMessage = MessageBuilder.withPayload(resultObject).copyHeaders(mappedMessageHeaders).build(); - return siMessage; + if (message instanceof SoapMessage){ + Map mappedMessageHeaders = + AbstractWebServiceOutboundGateway.this.headerMapper.toHeadersFromReply((SoapMessage) message); + return MessageBuilder.withPayload(resultObject).copyHeaders(mappedMessageHeaders).build(); } else { return resultObject; @@ -262,34 +239,7 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro } public abstract Object doExtractData(WebServiceMessage message) throws IOException, TransformerException; - } - /** - * 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); - } - - @SuppressWarnings("deprecation") - @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/config/WebServiceOutboundGatewayParser.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/WebServiceOutboundGatewayParser.java index d804af4968..431dbd2c56 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-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -20,9 +20,7 @@ import java.util.List; import org.w3c.dom.Element; -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.ManagedMap; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.integration.config.ExpressionFactoryBean; @@ -32,6 +30,8 @@ import org.springframework.integration.ws.DefaultSoapHeaderMapper; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; +import org.springframework.integration.ws.MarshallingWebServiceOutboundGateway; +import org.springframework.integration.ws.SimpleWebServiceOutboundGateway; /** * Parser for the <outbound-gateway/> element in the 'ws' namespace. @@ -39,18 +39,15 @@ import org.springframework.util.xml.DomUtils; * @author Mark Fisher * @author Jonas Partner * @author Gunnar Hillert + * @author Artem Bilan * */ public class WebServiceOutboundGatewayParser extends AbstractOutboundGatewayParser { - private static final String BASE_PACKAGE = "org.springframework.integration.ws"; - - @Override protected String getGatewayClassName(Element element) { - String simpleClassName = (StringUtils.hasText(element.getAttribute("marshaller"))) ? - "MarshallingWebServiceOutboundGateway" : "SimpleWebServiceOutboundGateway"; - return BASE_PACKAGE + "." + simpleClassName; + return ((StringUtils.hasText(element.getAttribute("marshaller"))) ? + MarshallingWebServiceOutboundGateway.class : SimpleWebServiceOutboundGateway.class).getName(); } @Override @@ -117,14 +114,9 @@ public class WebServiceOutboundGatewayParser extends AbstractOutboundGatewayPars if (StringUtils.hasText(messageFactoryRef)) { builder.addConstructorArgReference(messageFactoryRef); } - String requestCallbackRef = element.getAttribute("request-callback"); - if (StringUtils.hasText(requestCallbackRef)) { - builder.addPropertyReference("requestCallback", requestCallbackRef); - } - String faultMessageResolverRef = element.getAttribute("fault-message-resolver"); - if (StringUtils.hasText(faultMessageResolverRef)) { - builder.addPropertyReference("faultMessageResolver", faultMessageResolverRef); - } + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "request-callback"); + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "fault-message-resolver"); + String messageSenderRef = element.getAttribute("message-sender"); String messageSenderListRef = element.getAttribute("message-senders"); if (StringUtils.hasText(messageSenderRef) && StringUtils.hasText(messageSenderListRef)) { @@ -144,9 +136,7 @@ public class WebServiceOutboundGatewayParser extends AbstractOutboundGatewayPars "Only one of interceptor or interceptors should be specified.", element); } if (StringUtils.hasText(interceptorRef)) { - ManagedList interceptors = new ManagedList(); - interceptors.add(new RuntimeBeanReference(interceptorRef)); - builder.addPropertyValue("interceptors", interceptors); + builder.addPropertyReference("interceptors", interceptorRef); } if (StringUtils.hasText(interceptorListRef)) { builder.addPropertyReference("interceptors", interceptorListRef); diff --git a/spring-integration-ws/src/test/java/org/springframework/integration/ws/SimpleWebServiceOutboundGatewayTests.java b/spring-integration-ws/src/test/java/org/springframework/integration/ws/SimpleWebServiceOutboundGatewayTests.java index 3f3f618fe2..f299801e9e 100644 --- a/spring-integration-ws/src/test/java/org/springframework/integration/ws/SimpleWebServiceOutboundGatewayTests.java +++ b/spring-integration-ws/src/test/java/org/springframework/integration/ws/SimpleWebServiceOutboundGatewayTests.java @@ -16,6 +16,10 @@ package org.springframework.integration.ws; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.URI; @@ -43,8 +47,6 @@ import org.springframework.ws.soap.SoapMessage; import org.springframework.ws.transport.WebServiceConnection; import org.springframework.ws.transport.WebServiceMessageSender; -import static org.junit.Assert.*; - /** * @author Mark Fisher * @author Artem Bilan 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 index 7fff586b7f..8ca7a40357 100644 --- 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 @@ -1,17 +1,76 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 index 58c7697282..01735eaf9a 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2013 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. @@ -19,31 +19,48 @@ package org.springframework.integration.ws.config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import javax.jms.BytesMessage; +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.DeliveryMode; +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.Session; + +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.packet.Packet; import org.junit.Test; import org.junit.runner.RunWith; - +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; 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.integration.test.util.TestUtils; 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.WebServiceTransportException; import org.springframework.ws.client.support.interceptor.ClientInterceptor; import org.springframework.ws.context.MessageContext; import org.springframework.ws.soap.SoapMessageCreationException; +import org.springframework.ws.transport.WebServiceConnection; +import org.springframework.ws.transport.WebServiceMessageSender; import org.springframework.ws.transport.context.TransportContext; import org.springframework.ws.transport.context.TransportContextHolder; -import org.springframework.ws.transport.http.HttpUrlConnection; +import org.springframework.ws.transport.mail.MailSenderConnection; /** * @author Mark Fisher + * @author Artem Bilan * @since 2.1 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -51,15 +68,40 @@ import org.springframework.ws.transport.http.HttpUrlConnection; public class UriVariableTests { @Autowired - private ApplicationContext context; + private TestClientInterceptor interceptor; + + @Autowired + private MessageChannel inputHttp; + + @Autowired + private MessageChannel inputJms; + + @Autowired + private WebServiceMessageSender jmsMessageSender; + + @Autowired + private ConnectionFactory jmsConnectionFactory; + + @Autowired + private MessageChannel inputXmpp; + + @Autowired + private XMPPConnection xmppConnection; + + @Autowired + private MessageChannel inputEmail; + + @Autowired + private Int2720EmailTestClientInterceptor emailInterceptor; @Test - public void checkUriVariables() { - MessageChannel input = context.getBean("input", MessageChannel.class); - TestClientInterceptor interceptor = context.getBean("interceptor", TestClientInterceptor.class); - Message message = MessageBuilder.withPayload("").setHeader("x", "integration").build(); + public void testHttpUriVariables() { + Message message = MessageBuilder.withPayload("") + .setHeader("x", "integration") + .setHeader("param", "test1 & test2") + .build(); try { - input.send(message); + this.inputHttp.send(message); } catch (MessageHandlingException e) { // expected @@ -67,30 +109,117 @@ public class UriVariableTests { assertTrue(WebServiceIOException.class.equals(causeType) // offline || SoapMessageCreationException.class.equals(causeType)); } - assertEquals("http://springsource.org/spring-integration", interceptor.getLastUri().toString()); + assertEquals("http://test.org/spring-integration?param=test1%20%26%20test2", this.interceptor.getLastUri().toString()); } + @Test + public void testInt2720JmsUriVariables() throws JMSException, IOException { + final String destinationName = "SPRING.INTEGRATION.QUEUE"; + + Queue queue = Mockito.mock(Queue.class); + // Need for 'QueueSession#createQueue()' + Mockito.when(queue.getQueueName()).thenReturn(destinationName); + + Session session = Mockito.mock(Session.class); + Mockito.when(session.createQueue(Mockito.anyString())).thenReturn(queue); + Mockito.when(session.createBytesMessage()).thenReturn(Mockito.mock(BytesMessage.class)); + MessageProducer producer = Mockito.mock(MessageProducer.class); + Mockito.when(session.createProducer(queue)).thenReturn(producer); + // For this test it's enough to not go ahead. Invoked in the 'JmsSenderConnection#onSendAfterWrite' on the + // 'WebServiceTemplate#sendRequest' after invocation of our 'TestClientInterceptor' + Mockito.when(session.createTemporaryQueue()).thenThrow(new WebServiceIOException("intentional")); + + Connection connection = Mockito.mock(Connection.class); + Mockito.when(connection.createSession(Mockito.anyBoolean(), Mockito.anyInt())).thenReturn(session); + Mockito.when(this.jmsConnectionFactory.createConnection()).thenReturn(connection); + + Message message = MessageBuilder.withPayload("") + .setHeader("jmsQueue", destinationName) + .setHeader("deliveryMode", "NON_PERSISTENT") + .setHeader("jms_priority", "5") + .build(); + try { + this.inputJms.send(message); + } + catch (MessageHandlingException e) { + // expected + Class causeType = e.getCause().getClass(); + assertTrue(WebServiceIOException.class.equals(causeType)); // offline + } + URI uri = URI.create("jms:SPRING.INTEGRATION.QUEUE?deliveryMode=NON_PERSISTENT&priority=5"); + Mockito.verify(this.jmsMessageSender).createConnection(uri); + + Mockito.verify(session).createQueue(destinationName); + + assertEquals("jms:" + destinationName, this.interceptor.getLastUri().toString()); + Mockito.verify(producer).setDeliveryMode(DeliveryMode.NON_PERSISTENT); + Mockito.verify(producer).setPriority(5); + } + + @Test + public void testInt2720EmailUriVariables() { + final String testEmailTo = "user@example.com"; + final String testEmailSubject = "Test subject"; + Message message = MessageBuilder.withPayload("") + .setHeader("to", testEmailTo) + .setHeader("subject", testEmailSubject) + .build(); + try { + this.inputEmail.send(message); + } + catch (MessageHandlingException e) { + // expected + Class causeType = e.getCause().getClass(); + assertTrue(WebServiceTransportException.class.equals(causeType)); // offline + } + WebServiceConnection webServiceConnection = this.emailInterceptor.getLastWebServiceConnection(); + assertEquals(testEmailTo, TestUtils.getPropertyValue(webServiceConnection, "to").toString()); + assertEquals(testEmailSubject, TestUtils.getPropertyValue(webServiceConnection, "subject")); + assertEquals("mailto:user@example.com?subject=Test%20subject", this.emailInterceptor.getLastUri().toString()); + } + + @Test + public void testInt2720XmppUriVariables() { + + Mockito.doThrow(new WebServiceIOException("intentional")).when(this.xmppConnection).sendPacket(Mockito.any(Packet.class)); + + Message message = MessageBuilder.withPayload("").setHeader("to", "user").build(); + try { + this.inputXmpp.send(message); + } + catch (MessageHandlingException e) { + // expected + Class causeType = e.getCause().getClass(); + assertTrue(WebServiceIOException.class.equals(causeType)); // offline + } + + ArgumentCaptor argument = ArgumentCaptor.forClass(Packet.class); + Mockito.verify(this.xmppConnection).sendPacket(argument.capture()); + assertEquals("user@jabber.org", argument.getValue().getTo()); + + assertEquals("xmpp:user@jabber.org", this.interceptor.getLastUri().toString()); + } private static class TestClientInterceptor implements ClientInterceptor { - private URI lastUri; + private volatile URI lastUri; - private URI getLastUri() { + public URI getLastUri() { return this.lastUri; } public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException { TransportContext tc = TransportContextHolder.getTransportContext(); - if (tc != null && tc.getConnection() instanceof HttpUrlConnection) { + if (tc != null) { try { - this.lastUri = ((HttpUrlConnection) tc.getConnection()).getUri(); + this.lastUri = tc.getConnection().getUri(); } catch (URISyntaxException e) { throw new IllegalStateException(e); } } else { - throw new IllegalStateException("expected HttpUrlConnection as TransportContext"); + throw new IllegalStateException("expected WebServiceConnection in the TransportContext"); } return false; } @@ -104,4 +233,27 @@ public class UriVariableTests { } } + private static class Int2720EmailTestClientInterceptor extends TestClientInterceptor { + + private volatile WebServiceConnection webServiceConnection; + + public WebServiceConnection getLastWebServiceConnection() { + return webServiceConnection; + } + + @Override + public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException { + TransportContext tc = TransportContextHolder.getTransportContext(); + WebServiceConnection webServiceConnection = tc.getConnection(); + if (webServiceConnection instanceof MailSenderConnection) { + this.webServiceConnection = webServiceConnection; + } + else { + throw new IllegalStateException("expected MailSenderConnection in the TransportContext"); + } + + return super.handleRequest(messageContext); + } + } + } 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 20df5efefe..6f32183bcd 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 @@ -18,7 +18,6 @@ package org.springframework.integration.ws.config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import java.net.URI; @@ -43,11 +42,11 @@ import org.springframework.integration.ws.SimpleWebServiceOutboundGateway; 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.support.destination.DestinationProvider; import org.springframework.ws.client.support.interceptor.ClientInterceptor; import org.springframework.ws.transport.WebServiceMessageSender; @@ -399,9 +398,9 @@ public class WebServiceOutboundGatewayParserTests { AbstractEndpoint endpoint = (AbstractEndpoint) context.getBean("gatewayWithJmsUri"); assertEquals(EventDrivenConsumer.class, endpoint.getClass()); MessageHandler handler = TestUtils.getPropertyValue(endpoint, "handler", MessageHandler.class); - DestinationProvider destinationProvider = TestUtils.getPropertyValue(handler, "destinationProvider", DestinationProvider.class); - assertNotNull(destinationProvider); - assertEquals(URI.create("jms:wsQueue"), destinationProvider.getDestination()); + assertNull(TestUtils.getPropertyValue(handler, "destinationProvider")); + UriTemplate uriTemplate = TestUtils.getPropertyValue(handler, "uriTemplate", UriTemplate.class); + assertEquals(URI.create("jms:wsQueue"), uriTemplate.expand()); } @Test(expected = BeanDefinitionParsingException.class) diff --git a/spring-integration-ws/src/test/resources/.svnignore b/spring-integration-ws/src/test/resources/.svnignore deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/reference/docbook/index.xml b/src/reference/docbook/index.xml index c6f8d2bbe7..ebfbc8f431 100644 --- a/src/reference/docbook/index.xml +++ b/src/reference/docbook/index.xml @@ -24,48 +24,37 @@ Mark Fisher - - + Marius Bogoevici - - + Iwein Fuld - - + Jonas Partner - - + Oleg Zhurakousky - - + Gary Russell - - + Dave Syer - - + Josh Long - - + David Turanski - - + Gunnar Hillert - - + Artem Bilan - - + Amol Nayak diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml index ce0b43f2a9..15ab58f8ba 100644 --- a/src/reference/docbook/whats-new.xml +++ b/src/reference/docbook/whats-new.xml @@ -71,7 +71,13 @@ payloads to String. For more information see . - +
+ Web Service Outbound URI Configuration + + Web Service Outbound Gateway 'uri' attribute now supports <uri-variable/> substitution for all + URI-schemes supported by Spring Web Services. For more information see . + +
diff --git a/src/reference/docbook/ws.xml b/src/reference/docbook/ws.xml index bed205f37c..b963fcd74a 100644 --- a/src/reference/docbook/ws.xml +++ b/src/reference/docbook/ws.xml @@ -7,7 +7,7 @@ Outbound Web Service Gateways To invoke a Web Service upon sending a message to a channel, there are two options - both of which build - upon the Spring Web Services + upon the Spring Web Services project: SimpleWebServiceOutboundGateway and MarshallingWebServiceOutboundGateway. The former will accept either a String or javax.xml.transform.Source as the message @@ -21,10 +21,10 @@ When using the namespace support described below, you will only need to set a URI. Internally, the parser will configure a fixed URI DestinationProvider implementation. If you do need dynamic resolution of the - URI at runtime, however, then the DestinationProvider can provide such behavior as looking up the URI from - a registry. See the Spring Web Services - javadoc for - more information about the DestinationProvider strategy. + URI at runtime, however, then the DestinationProvider can provide such behavior as looking up the URI from + a registry. See the Spring Web Services + DestinationProvider + JavaDoc for more information about this strategy. @@ -59,7 +59,7 @@ as per standard Spring Web Services configuration. For more detail on how to use these components, see the Spring Web Services reference guide's chapter covering - creating a Web Service. + creating a Web Service. The chapter covering Object/XML mapping is also applicable again. @@ -133,17 +133,26 @@ as per standard Spring Web Services configuration.
Outbound URI Configuration - For URIs with an http: (or https:) scheme, - <uri-variable/> substitution is supported: + For all URI-schemes supported by Spring Web Services + (URIs and Transports) + <uri-variable/> substitution is provided: - + + + + + + + ]]> - - For other schemes, such as jms:, or if a DestinationProvider - is supplied, variable substitution is not supported and a configuration error will result if variables - are provided. - + + If a DestinationProvider is supplied, variable substitution is not supported + and a configuration error will result if variables are provided. +