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 <authorgroup> 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
This commit is contained in:
Artem Bilan
2012-08-27 17:59:06 +03:00
committed by Gary Russell
parent 7d99a9a07a
commit 6b7ae6ceed
11 changed files with 332 additions and 166 deletions

View File

@@ -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

View File

@@ -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<String, Expression> uriVariableExpressions = new HashMap<String, Expression>();
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<Object> {
@@ -251,10 +228,10 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro
Object resultObject = this.doExtractData(message);
if (message instanceof SoapMessage){
Map<String, Object> mappedMessageHeaders = headerMapper.toHeadersFromReply((SoapMessage) message);
Message<?> siMessage = MessageBuilder.withPayload(resultObject).copyHeaders(mappedMessageHeaders).build();
return siMessage;
if (message instanceof SoapMessage){
Map<String, Object> 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);
}
}
}
}

View File

@@ -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 &lt;outbound-gateway/&gt; 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<RuntimeBeanReference> interceptors = new ManagedList<RuntimeBeanReference>();
interceptors.add(new RuntimeBeanReference(interceptorRef));
builder.addPropertyValue("interceptors", interceptors);
builder.addPropertyReference("interceptors", interceptorRef);
}
if (StringUtils.hasText(interceptorListRef)) {
builder.addPropertyReference("interceptors", interceptorListRef);

View File

@@ -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

View File

@@ -1,17 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ws="http://www.springframework.org/schema/integration/ws"
xsi:schemaLocation="http://www.springframework.org/schema/beans
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ws="http://www.springframework.org/schema/integration/ws"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/ws
http://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd">
<ws:outbound-gateway id="gateway" request-channel="input" uri="http://springsource.org/{foo}-{bar}" interceptor="interceptor">
<ws:uri-variable name="foo" expression="payload.substring(1,7)"/>
<ws:uri-variable name="bar" expression="headers.x"/>
</ws:outbound-gateway>
<bean id="interceptor" class="org.springframework.integration.ws.config.UriVariableTests$TestClientInterceptor"/>
<!--HTTP Transport-->
<ws:outbound-gateway request-channel="inputHttp"
uri="http://test.org/{foo}-{bar}?param={param}"
interceptor="interceptor">
<ws:uri-variable name="foo" expression="payload.substring(1,7)"/>
<ws:uri-variable name="bar" expression="headers.x"/>
<ws:uri-variable name="param" expression="headers.param"/>
</ws:outbound-gateway>
<!--JMS Transport-->
<ws:outbound-gateway request-channel="inputJms"
uri="jms:{destination}?deliveryMode={deliveryMode}&amp;priority={priority}"
interceptor="interceptor"
message-sender="jmsMessageSender">
<ws:uri-variable name="destination" expression="headers.jmsQueue"/>
<ws:uri-variable name="deliveryMode" expression="headers.deliveryMode"/>
<ws:uri-variable name="priority" expression="headers.jms_priority"/>
</ws:outbound-gateway>
<bean id="jmsMessageSender" class="org.mockito.Mockito" factory-method="spy">
<constructor-arg>
<bean class="org.springframework.ws.transport.jms.JmsMessageSender">
<property name="connectionFactory" ref="jmsConnectionFactory"/>
</bean>
</constructor-arg>
</bean>
<bean id="jmsConnectionFactory" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="javax.jms.ConnectionFactory"/>
</bean>
<!--Email Transport-->
<ws:outbound-gateway request-channel="inputEmail"
uri="mailto:{to}?subject={subject}"
interceptor="emailInterceptor"
message-sender="emailMessageSender">
<ws:uri-variable name="to" expression="headers.to"/>
<ws:uri-variable name="subject" expression="headers.subject"/>
</ws:outbound-gateway>
<bean id="emailMessageSender" class="org.springframework.ws.transport.mail.MailMessageSender">
<property name="from" value="spring-integration-ws SOAP Client &lt;client@example.com&gt;"/>
<property name="transportUri" value="smtp://client:s04p@smtp.example.com"/>
<property name="storeUri" value="imap://client:s04p@imap.example.com/INBOX"/>
</bean>
<bean id="emailInterceptor" class="org.springframework.integration.ws.config.UriVariableTests$Int2720EmailTestClientInterceptor"/>
<!--XMPP Transport-->
<ws:outbound-gateway request-channel="inputXmpp"
uri="xmpp:{user}@jabber.org"
interceptor="interceptor"
message-sender="xmppMessageSender">
<ws:uri-variable name="user" expression="headers.to"/>
</ws:outbound-gateway>
<bean id="xmppMessageSender" class="org.springframework.ws.transport.xmpp.XmppMessageSender">
<property name="connection" ref="xmppConnection"/>
</bean>
<bean id="xmppConnection" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.jivesoftware.smack.XMPPConnection"/>
</bean>
</beans>

View File

@@ -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("<spring/>").setHeader("x", "integration").build();
public void testHttpUriVariables() {
Message<?> message = MessageBuilder.withPayload("<spring/>")
.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("<spring/>")
.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("<spring/>")
.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("<spring/>").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<Packet> 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);
}
}
}

View File

@@ -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)

View File

@@ -24,48 +24,37 @@
<author>
<firstname>Mark</firstname>
<surname>Fisher</surname>
</author>
<author>
</author><author>
<firstname>Marius</firstname>
<surname>Bogoevici</surname>
</author>
<author>
</author><author>
<firstname>Iwein</firstname>
<surname>Fuld</surname>
</author>
<author>
</author><author>
<firstname>Jonas</firstname>
<surname>Partner</surname>
</author>
<author>
</author><author>
<firstname>Oleg</firstname>
<surname>Zhurakousky</surname>
</author>
<author>
</author><author>
<firstname>Gary</firstname>
<surname>Russell</surname>
</author>
<author>
</author><author>
<firstname>Dave</firstname>
<surname>Syer</surname>
</author>
<author>
</author><author>
<firstname>Josh</firstname>
<surname>Long</surname>
</author>
<author>
</author><author>
<firstname>David</firstname>
<surname>Turanski</surname>
</author>
<author>
</author><author>
<firstname>Gunnar</firstname>
<surname>Hillert</surname>
</author>
<author>
</author><author>
<firstname>Artem</firstname>
<surname>Bilan</surname>
</author>
<author>
</author><author>
<firstname>Amol</firstname>
<surname>Nayak</surname>
</author>

View File

@@ -71,7 +71,13 @@
payloads to <classname>String</classname>. For more information see <xref linkend="transformer"/>.
</para>
</section>
<section id="3.0-ws-outbound-uri-substitution">
<title>Web Service Outbound URI Configuration</title>
<para>
Web Service Outbound Gateway 'uri' attribute now supports <code>&lt;uri-variable/&gt;</code> substitution for all
URI-schemes supported by Spring Web Services. For more information see <xref linkend="outbound-uri"/>.
</para>
</section>
</section>
</chapter>

View File

@@ -7,7 +7,7 @@
<title>Outbound Web Service Gateways</title>
<para>
To invoke a Web Service upon sending a message to a channel, there are two options - both of which build
upon the <ulink url="http://static.springframework.org/spring-ws/sites/1.5/">Spring Web Services</ulink>
upon the <ulink url="http://static.springsource.org/spring-ws/site/">Spring Web Services</ulink>
project: <classname>SimpleWebServiceOutboundGateway</classname> and
<classname>MarshallingWebServiceOutboundGateway</classname>. The former will accept either a
<classname>String</classname> or <interfacename>javax.xml.transform.Source</interfacename> as the message
@@ -21,10 +21,10 @@
<note>
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
<ulink url="http://static.springsource.org/spring-ws/sites/1.5/apidocs/index.html">javadoc</ulink> 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
<ulink url="http://static.springsource.org/spring-ws/site/apidocs/org/springframework/ws/client/support/destination/DestinationProvider.html">DestinationProvider</ulink>
JavaDoc for more information about this strategy.
</note>
</para>
<para>
@@ -59,7 +59,7 @@ as per standard Spring Web Services configuration.
</para>
<para>
For more detail on how to use these components, see the Spring Web Services reference guide's chapter covering
<ulink url="http://static.springframework.org/spring-ws/sites/1.5/reference/html/server.html">creating a Web Service</ulink>.
<ulink url="http://static.springframework.org/spring-ws/site/reference/html/server.html">creating a Web Service</ulink>.
The chapter covering
<ulink url="http://static.springframework.org/spring-ws/site/reference/html/oxm.html">Object/XML mapping</ulink> is also applicable again.
</para>
@@ -133,17 +133,26 @@ as per standard Spring Web Services configuration.
<section id="outbound-uri">
<title>Outbound URI Configuration</title>
<para>
For URIs with an <emphasis>http:</emphasis> (or <emphasis>https:</emphasis>) scheme,
&lt;uri-variable/&gt; substitution is supported:
For all URI-schemes supported by Spring Web Services
(<ulink url="http://static.springsource.org/spring-ws/site/reference/html/client.html#client-transports">URIs and Transports</ulink>)
<code>&lt;uri-variable/&gt;</code> substitution is provided:
</para>
<programlisting language="xml"><![CDATA[<ws:outbound-gateway id="gateway" request-channel="input" uri="http://springsource.org/{foo}-{bar}">
<programlisting language="xml"><![CDATA[<ws:outbound-gateway id="gateway" request-channel="input"
uri="http://springsource.org/{foo}-{bar}">
<ws:uri-variable name="foo" expression="payload.substring(1,7)"/>
<ws:uri-variable name="bar" expression="headers.x"/>
</ws:outbound-gateway>
<ws:outbound-gateway request-channel="inputJms"
uri="jms:{destination}?deliveryMode={deliveryMode}&amp;priority={priority}"
message-sender="jmsMessageSender">
<ws:uri-variable name="destination" expression="headers.jmsQueue"/>
<ws:uri-variable name="deliveryMode" expression="headers.deliveryMode"/>
<ws:uri-variable name="priority" expression="headers.jms_priority"/>
</ws:outbound-gateway>]]></programlisting>
<para>
For other schemes, such as <emphasis>jms:</emphasis>, or if a <classname>DestinationProvider</classname>
is supplied, variable substitution is not supported and a configuration error will result if variables
are provided.
</para>
<para>
If a <classname>DestinationProvider</classname> is supplied, variable substitution is not supported
and a configuration error will result if variables are provided.
</para>
</section>
</chapter>