From 5afd12a819b1a90cfc92b01cc250198af28a4184 Mon Sep 17 00:00:00 2001 From: Oleg Zhurakousky Date: Mon, 8 Aug 2011 17:21:17 -0400 Subject: [PATCH] INT-1951, INT-1981 refactored WS module to remove dependency on deprecated version of Spring-WS, added validation checks to the Inbound Gateway Parser for 'extract-payload' attribute --- .../ws/AbstractWebServiceInboundGateway.java | 91 ++++++++ .../MarshallingWebServiceInboundGateway.java | 212 ++++-------------- .../ws/SimpleWebServiceInboundGateway.java | 74 ++---- .../WebServiceInboundGatewayParser.java | 22 +- .../ws/config/spring-integration-ws-2.1.xsd | 2 +- .../WebServiceInboundGatewayParserTests.java | 7 +- 6 files changed, 177 insertions(+), 231 deletions(-) create mode 100644 spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceInboundGateway.java diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceInboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceInboundGateway.java new file mode 100644 index 0000000000..309d630b1f --- /dev/null +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/AbstractWebServiceInboundGateway.java @@ -0,0 +1,91 @@ +/* + * 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; + +import java.util.Map; + +import org.springframework.expression.ExpressionException; +import org.springframework.integration.Message; +import org.springframework.integration.MessagingException; +import org.springframework.integration.gateway.MessagingGatewaySupport; +import org.springframework.integration.mapping.HeaderMapper; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.ws.WebServiceMessage; +import org.springframework.ws.context.MessageContext; +import org.springframework.ws.server.endpoint.MessageEndpoint; +import org.springframework.ws.soap.SoapHeader; +import org.springframework.ws.soap.SoapMessage; + +/** + * @author Oleg Zhurakousky + * @since 2.1 + */ +abstract public class AbstractWebServiceInboundGateway extends MessagingGatewaySupport implements MessageEndpoint { + + protected volatile HeaderMapper headerMapper = new DefaultSoapHeaderMapper(); + + public String getComponentType() { + return "ws:outbound-gateway"; + } + + public void setHeaderMapper(HeaderMapper headerMapper) { + Assert.notNull(headerMapper, "headerMapper must not be null"); + this.headerMapper = headerMapper; + } + + public void invoke(MessageContext messageContext) throws Exception { + Assert.notNull(messageContext,"'messageContext' is required; it must not be null."); + + try { + this.doInvoke(messageContext); + } + catch (Exception e) { + while ((e instanceof MessagingException || e instanceof ExpressionException) && + e.getCause() instanceof Exception) { + e = (Exception) e.getCause(); + } + throw e; + } + } + + protected void fromSoapHeaders(MessageContext messageContext, MessageBuilder builder){ + WebServiceMessage request = messageContext.getRequest(); + String[] propertyNames = messageContext.getPropertyNames(); + if (propertyNames != null) { + for (String propertyName : propertyNames) { + builder.setHeader(propertyName, messageContext.getProperty(propertyName)); + } + } + if (request instanceof SoapMessage) { + SoapMessage soapMessage = (SoapMessage) request; + Map headers = this.headerMapper.toHeaders(soapMessage.getSoapHeader()); + if (!CollectionUtils.isEmpty(headers)) { + builder.copyHeaders(headers); + } + } + } + + protected void toSoapHeaders(WebServiceMessage response, Message replyMessage){ + if (response instanceof SoapMessage) { + this.headerMapper.fromHeaders( + replyMessage.getHeaders(), ((SoapMessage) response).getSoapHeader()); + } + } + + abstract protected void doInvoke(MessageContext messageContext) throws Exception; +} diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceInboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceInboundGateway.java index 10603fc442..edf5f042f3 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceInboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/MarshallingWebServiceInboundGateway.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,37 +16,25 @@ package org.springframework.integration.ws; -import java.util.concurrent.locks.ReentrantLock; - -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.SmartLifecycle; -import org.springframework.expression.ExpressionException; -import org.springframework.integration.MessageChannel; -import org.springframework.integration.MessagingException; -import org.springframework.integration.gateway.MessagingGatewaySupport; -import org.springframework.integration.history.TrackableComponent; +import org.springframework.integration.Message; +import org.springframework.integration.support.MessageBuilder; import org.springframework.oxm.Marshaller; import org.springframework.oxm.Unmarshaller; -import org.springframework.scheduling.TaskScheduler; -import org.springframework.ws.server.endpoint.AbstractMarshallingPayloadEndpoint; +import org.springframework.util.Assert; +import org.springframework.ws.WebServiceMessage; +import org.springframework.ws.context.MessageContext; +import org.springframework.ws.support.MarshallingUtils; /** * @author Mark Fisher * @author Oleg Zhurakousky * @since 1.0.2 */ -public class MarshallingWebServiceInboundGateway extends AbstractMarshallingPayloadEndpoint - implements BeanNameAware, BeanFactoryAware, InitializingBean, SmartLifecycle, TrackableComponent { - - private final ReentrantLock lifecycleLock = new ReentrantLock(); - - private final GatewayDelegate gatewayDelegate = new GatewayDelegate(); - - private volatile int phase = 0; +public class MarshallingWebServiceInboundGateway extends AbstractWebServiceInboundGateway { + + private volatile Marshaller marshaller; + private volatile Unmarshaller unmarshaller; /** * Creates a new MarshallingWebServiceInboundGateway. @@ -68,161 +56,55 @@ public class MarshallingWebServiceInboundGateway extends AbstractMarshallingPayl * @see #MarshallingWebServiceInboundGateway(Marshaller, Unmarshaller) */ public MarshallingWebServiceInboundGateway(Marshaller marshaller) { - super(marshaller); + Assert.notNull(marshaller, "'marshaller' must no be null"); + Assert.isInstanceOf(Unmarshaller.class, marshaller, "When using this constructor the provided " + + "Marshaller must also implement Unmarshaller"); + this.marshaller = marshaller; + this.unmarshaller = unmarshaller; } /** * Creates a new MarshallingWebServiceInboundGateway with the given marshaller and unmarshaller. */ public MarshallingWebServiceInboundGateway(Marshaller marshaller, Unmarshaller unmarshaller) { - super(marshaller, unmarshaller); + Assert.notNull(marshaller, "'marshaller' must no be null"); + Assert.notNull(unmarshaller, "'unmarshaller' must no be null"); + this.marshaller = marshaller; + this.unmarshaller = unmarshaller; } + public void setMarshaller(Marshaller marshaller) { + Assert.notNull(marshaller, "'marshaller' must no be null"); + this.marshaller = marshaller; + } - public void setRequestChannel(MessageChannel requestChannel) { - this.gatewayDelegate.setRequestChannel(requestChannel); - } - - public void setRequestTimeout(long requestTimeout) { - this.gatewayDelegate.setRequestTimeout(requestTimeout); + public void setUnmarshaller(Unmarshaller unmarshaller) { + Assert.notNull(unmarshaller, "'unmarshaller' must no be null"); + this.unmarshaller = unmarshaller; } - public void setErrorChannel(MessageChannel errorChannel) { - this.gatewayDelegate.setErrorChannel(errorChannel); + protected void onInit() throws Exception { + super.onInit(); + Assert.notNull(marshaller, "This implementation requires Marshaller"); + Assert.notNull(unmarshaller, "This implementation requires Unmarshaller"); } - public void setReplyChannel(MessageChannel replyChannel) { - this.gatewayDelegate.setReplyChannel(replyChannel); - } - - public void setReplyTimeout(long replyTimeout) { - this.gatewayDelegate.setReplyTimeout(replyTimeout); - } - - public void setTaskScheduler(TaskScheduler taskScheduler) { - this.gatewayDelegate.setTaskScheduler(taskScheduler); - } - - public void setShouldTrack(boolean shouldTrack) { - this.gatewayDelegate.setShouldTrack(shouldTrack); - } - - public String getComponentName() { - return this.gatewayDelegate.getComponentName(); - } - - public String getComponentType() { - return this.gatewayDelegate.getComponentType(); - } - - public void setAutoStartup(boolean autoStartup) { - this.gatewayDelegate.setAutoStartup(autoStartup); - } - - public boolean isAutoStartup() { - return this.gatewayDelegate.isAutoStartup(); - } - - public void setPhase(int phase) { - this.phase = phase; - } - - public int getPhase() { - return this.phase; - } - - public void setBeanName(String beanName) { - this.gatewayDelegate.setBeanName(beanName); - } - - public void setBeanFactory(BeanFactory beanFactory) { - this.gatewayDelegate.setBeanFactory(beanFactory); - } - - public void afterPropertiesSet() throws Exception { - super.afterPropertiesSet(); - this.gatewayDelegate.afterPropertiesSet(); - } - - @Override - protected Object invokeInternal(Object requestObject) throws Exception { - try { - return this.gatewayDelegate.sendAndReceive(requestObject); - } - catch (Exception e) { - while ((e instanceof MessagingException || e instanceof ExpressionException) && - e.getCause() instanceof Exception) { - e = (Exception) e.getCause(); - } - throw e; - } + protected void doInvoke(MessageContext messageContext) throws Exception{ + WebServiceMessage request = messageContext.getRequest(); + Assert.notNull(request, "Invalid message context: request was null."); + Object requestObject = MarshallingUtils.unmarshal(unmarshaller, request); + MessageBuilder builder = MessageBuilder.withPayload(requestObject); + + this.fromSoapHeaders(messageContext, builder); + + Message replyMessage = this.sendAndReceiveMessage(builder.build()); + + if (replyMessage != null) { + WebServiceMessage response = messageContext.getResponse(); + this.toSoapHeaders(response, replyMessage); + + MarshallingUtils.marshal(marshaller, replyMessage.getPayload(), response); + } } - - - // Lifecycle implementation - - public boolean isRunning() { - this.lifecycleLock.lock(); - try { - return this.gatewayDelegate.isRunning(); - } - finally { - this.lifecycleLock.unlock(); - } - } - - public void start() { - this.lifecycleLock.lock(); - try { - if (!this.gatewayDelegate.isRunning()) { - this.gatewayDelegate.start(); - if (logger.isInfoEnabled()) { - logger.info("started " + this); - } - } - } - finally { - this.lifecycleLock.unlock(); - } - } - - public void stop() { - this.lifecycleLock.lock(); - try { - if (gatewayDelegate.isRunning()) { - this.gatewayDelegate.stop(); - if (logger.isInfoEnabled()) { - logger.info("stopped " + this); - } - } - } - finally { - this.lifecycleLock.unlock(); - } - } - - public void stop(Runnable callback) { - this.lifecycleLock.lock(); - try { - this.stop(); - callback.run(); - } - finally { - this.lifecycleLock.unlock(); - } - } - - - private static class GatewayDelegate extends MessagingGatewaySupport { - - public Object sendAndReceive(Object request) { - return super.sendAndReceive(request); - } - - public String getComponentType() { - return "ws:outbound-gateway"; - } - } - } diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceInboundGateway.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceInboundGateway.java index eb5e02a2f9..7363c9170a 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceInboundGateway.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/SimpleWebServiceInboundGateway.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,91 +16,48 @@ package org.springframework.integration.ws; -import java.util.Map; - import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.TransformerException; import javax.xml.transform.dom.DOMSource; -import org.w3c.dom.Document; - -import org.springframework.expression.ExpressionException; import org.springframework.integration.Message; -import org.springframework.integration.MessagingException; -import org.springframework.integration.gateway.MessagingGatewaySupport; -import org.springframework.integration.mapping.HeaderMapper; import org.springframework.integration.support.MessageBuilder; import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; import org.springframework.ws.WebServiceMessage; import org.springframework.ws.context.MessageContext; -import org.springframework.ws.server.endpoint.MessageEndpoint; -import org.springframework.ws.soap.SoapHeader; -import org.springframework.ws.soap.SoapMessage; import org.springframework.xml.transform.StringSource; import org.springframework.xml.transform.TransformerObjectSupport; +import org.w3c.dom.Document; /** * @author Mark Fisher + * @author Oleg Zhurakousky * @since 1.0.2 */ -public class SimpleWebServiceInboundGateway extends MessagingGatewaySupport implements MessageEndpoint { +public class SimpleWebServiceInboundGateway extends AbstractWebServiceInboundGateway { private final TransformerSupportDelegate transformerSupportDelegate = new TransformerSupportDelegate(); private volatile boolean extractPayload = true; - private volatile HeaderMapper headerMapper = new DefaultSoapHeaderMapper(); - - public void setExtractPayload(boolean extractPayload) { this.extractPayload = extractPayload; } - public void setHeaderMapper(HeaderMapper headerMapper) { - Assert.notNull(headerMapper, "headerMapper must not be null"); - this.headerMapper = headerMapper; - } - - public String getComponentType() { - return "ws:outbound-gateway"; - } - - public void invoke(MessageContext messageContext) throws Exception { - try { - this.doInvoke(messageContext); - } - catch (Exception e) { - while ((e instanceof MessagingException || e instanceof ExpressionException) && - e.getCause() instanceof Exception) { - e = (Exception) e.getCause(); - } - throw e; - } - } - - private void doInvoke(MessageContext messageContext) throws Exception { - Assert.notNull(messageContext,"'messageContext' is required; it must not be null."); + protected void doInvoke(MessageContext messageContext) throws Exception { + WebServiceMessage request = messageContext.getRequest(); Assert.notNull(request, "Invalid message context: request was null."); + MessageBuilder builder = MessageBuilder.withPayload( (this.extractPayload) ? request.getPayloadSource() : request); - String[] propertyNames = messageContext.getPropertyNames(); - if (propertyNames != null) { - for (String propertyName : propertyNames) { - builder.setHeader(propertyName, messageContext.getProperty(propertyName)); - } - } - if (request instanceof SoapMessage) { - SoapMessage soapMessage = (SoapMessage) request; - Map headers = this.headerMapper.toHeaders(soapMessage.getSoapHeader()); - if (!CollectionUtils.isEmpty(headers)) { - builder.copyHeaders(headers); - } - } + + this.fromSoapHeaders(messageContext, builder); + Message replyMessage = this.sendAndReceiveMessage(builder.build()); - if (replyMessage != null && replyMessage.getPayload() != null) { + + if (replyMessage != null) { Object replyPayload = replyMessage.getPayload(); Source responseSource = null; if (replyPayload instanceof Source) { @@ -119,11 +76,10 @@ public class SimpleWebServiceInboundGateway extends MessagingGatewaySupport impl + replyPayload.getClass().getName() + "]"); } WebServiceMessage response = messageContext.getResponse(); - if (response instanceof SoapMessage) { - this.headerMapper.fromHeaders( - replyMessage.getHeaders(), ((SoapMessage) response).getSoapHeader()); - } this.transformerSupportDelegate.transformSourceToResult(responseSource, response.getPayloadResult()); + + this.toSoapHeaders(response, replyMessage); + } } diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/WebServiceInboundGatewayParser.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/WebServiceInboundGatewayParser.java index 475a1be4a8..f292bb9d96 100644 --- a/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/WebServiceInboundGatewayParser.java +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/config/WebServiceInboundGatewayParser.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. @@ -18,6 +18,8 @@ package org.springframework.integration.ws.config; import org.w3c.dom.Element; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.integration.config.xml.AbstractInboundGatewayParser; import org.springframework.util.Assert; @@ -26,9 +28,10 @@ import org.springframework.util.StringUtils; /** * @author Iwein Fuld * @author Mark Fisher + * @author Oleg Zhurakousky */ public class WebServiceInboundGatewayParser extends AbstractInboundGatewayParser { - + protected final Log logger = LogFactory.getLog(getClass()); @Override protected String getBeanClassName(Element element) { String simpleClassName = (StringUtils.hasText(element.getAttribute("marshaller"))) ? @@ -44,13 +47,26 @@ public class WebServiceInboundGatewayParser extends AbstractInboundGatewayParser @Override protected void doPostProcess(BeanDefinitionBuilder builder, Element element) { String marshallerRef = element.getAttribute("marshaller"); + String unmarshallerRef = element.getAttribute("unmarshaller"); if (StringUtils.hasText(marshallerRef)) { builder.addConstructorArgReference(marshallerRef); - String unmarshallerRef = element.getAttribute("unmarshaller"); if (StringUtils.hasText(unmarshallerRef)) { builder.addConstructorArgReference(unmarshallerRef); } } + else { // check if unmarshaller is defined which is a mistake without marshaller + if (StringUtils.hasText(unmarshallerRef)){ + throw new IllegalArgumentException("Defining 'unmarshaller' without 'marshaller' is not allowed"); + } + } + + if (StringUtils.hasText(marshallerRef) || StringUtils.hasText(unmarshallerRef)){ + String extractPayload = element.getAttribute("extract-payload"); + if (StringUtils.hasText(extractPayload)){ + logger.warn("Setting 'extract-payload' attribute ihas no effect when used with MarshallingWebServiceInboundGateway"); + } + } + String headerMapperRef = element.getAttribute("header-mapper"); if (StringUtils.hasText(headerMapperRef)) { Assert.isTrue(!StringUtils.hasText(marshallerRef), 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 0ce610c4b8..260e9ebd89 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 @@ -260,7 +260,7 @@ - + diff --git a/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceInboundGatewayParserTests.java b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceInboundGatewayParserTests.java index 9e9b864d03..efa102ad76 100644 --- a/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceInboundGatewayParserTests.java +++ b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/WebServiceInboundGatewayParserTests.java @@ -117,6 +117,7 @@ public class WebServiceInboundGatewayParserTests { //marshalling @Autowired + @Qualifier("marshalling") MarshallingWebServiceInboundGateway marshallingGateway; @Autowired @@ -131,10 +132,10 @@ public class WebServiceInboundGatewayParserTests { is(marshaller)); assertTrue("messaging gateway is not running", marshallingGateway.isRunning()); - MessagingGatewaySupport mgs = (MessagingGatewaySupport) accessor.getPropertyValue("gatewayDelegate"); - DirectFieldAccessor mgsAccessor = new DirectFieldAccessor(mgs); + //MessagingGatewaySupport mgs = (MessagingGatewaySupport) accessor.getPropertyValue("gatewayDelegate"); + //DirectFieldAccessor mgsAccessor = new DirectFieldAccessor(mgs); assertThat( - (MessageChannel) mgsAccessor.getPropertyValue("errorChannel"), + (MessageChannel) accessor.getPropertyValue("errorChannel"), is(customErrorChannel)); }