diff --git a/spring-integration-ws/src/main/java/org/springframework/integration/ws/DefaultSoapHeaderMapper.java b/spring-integration-ws/src/main/java/org/springframework/integration/ws/DefaultSoapHeaderMapper.java new file mode 100644 index 0000000000..0908e0cda4 --- /dev/null +++ b/spring-integration-ws/src/main/java/org/springframework/integration/ws/DefaultSoapHeaderMapper.java @@ -0,0 +1,125 @@ +/* + * Copyright 2002-2010 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.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.xml.namespace.QName; + +import org.springframework.integration.MessageHeaders; +import org.springframework.integration.mapping.HeaderMapper; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.util.PatternMatchUtils; +import org.springframework.ws.soap.SoapHeader; +import org.springframework.ws.soap.SoapHeaderElement; +import org.springframework.xml.namespace.QNameUtils; + +/** + * A {@link HeaderMapper} implementation for mapping to and from a SoapHeader. + * The {@link #inboundHeaderNames} and {@link #outboundHeaderNames} may be configured. + * They accept exact name Strings or simple patterns (e.g. "start*", "*end", or "*"). + * By default all inbound headers will be accepted, but any outbound header that should + * be mapped must be configured explicitly. Note that the outbound mapping only writes + * String header values into attributes on the SoapHeader. For anything more advanced, + * one should implement the HeaderMapper interface directly. + * + * @author Mark Fisher + * @since 2.0 + */ +public class DefaultSoapHeaderMapper implements HeaderMapper { + + private volatile String[] outboundHeaderNames = new String[0]; + + private volatile String[] inboundHeaderNames = new String[] { "*" }; + + + public void setOutboundHeaderNames(String[] outboundHeaderNames) { + this.outboundHeaderNames = (outboundHeaderNames != null) ? outboundHeaderNames : new String[0]; + } + + public void setInboundHeaderNames(String[] inboundHeaderNames) { + this.inboundHeaderNames = (inboundHeaderNames != null) ? inboundHeaderNames : new String[0]; + } + + public void fromHeaders(MessageHeaders headers, SoapHeader target) { + if (target != null && !CollectionUtils.isEmpty(headers)) { + for (String headerName : headers.keySet()) { + if (this.shouldMapOutboundHeader(headerName)) { + Object value = headers.get(headerName); + if (value instanceof String) { + QName qname = QNameUtils.parseQNameString(headerName); + target.addAttribute(qname, (String) value); + } + } + } + } + } + + public Map toHeaders(SoapHeader source) { + Map headers = new HashMap(); + if (source != null) { + Iterator attributeIter = source.getAllAttributes(); + while (attributeIter.hasNext()) { + Object name = attributeIter.next(); + if (name instanceof QName) { + String qnameString = QNameUtils.toQualifiedName((QName) name); + if (this.shouldMapInboundHeader(qnameString)) { + String value = source.getAttributeValue((QName) name); + if (value != null) { + headers.put(qnameString, value); + } + } + } + } + Iterator elementIter = source.examineAllHeaderElements(); + while (elementIter.hasNext()) { + Object element = elementIter.next(); + if (element instanceof SoapHeaderElement) { + QName qname = ((SoapHeaderElement) element).getName(); + String qnameString = QNameUtils.toQualifiedName(qname); + if (this.shouldMapInboundHeader(qnameString)) { + headers.put(qnameString, element); + } + } + } + } + return headers; + } + + private boolean shouldMapInboundHeader(String headerName) { + return matchesAny(this.inboundHeaderNames, headerName); + } + + private boolean shouldMapOutboundHeader(String headerName) { + return matchesAny(this.outboundHeaderNames, headerName); + } + + private static boolean matchesAny(String[] patterns, String candidate) { + if (!ObjectUtils.isEmpty(patterns) && QNameUtils.validateQName(candidate)) { + for (String pattern : patterns) { + if (PatternMatchUtils.simpleMatch(pattern, candidate)) { + return true; + } + } + } + return false; + } + +} 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 e8d95a2a42..b058662872 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 @@ -43,7 +43,10 @@ public class MarshallingWebServiceInboundGateway extends AbstractMarshallingPayl private final ReentrantLock lifecycleLock = new ReentrantLock(); private final GatewayDelegate gatewayDelegate = new GatewayDelegate(); - + + private volatile int phase = 0; + + /** * Creates a new MarshallingWebServiceInboundGateway. * The {@link Marshaller} and {@link Unmarshaller} must be injected using properties. @@ -95,10 +98,34 @@ public class MarshallingWebServiceInboundGateway extends AbstractMarshallingPayl 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); } @@ -143,7 +170,7 @@ public class MarshallingWebServiceInboundGateway extends AbstractMarshallingPayl public void start() { this.lifecycleLock.lock(); try { - if (!gatewayDelegate.isRunning()) { + if (!this.gatewayDelegate.isRunning()) { this.gatewayDelegate.start(); if (logger.isInfoEnabled()) { logger.info("started " + this); @@ -170,10 +197,6 @@ public class MarshallingWebServiceInboundGateway extends AbstractMarshallingPayl } } - public boolean isAutoStartup() { - return true; - } - public void stop(Runnable callback) { this.lifecycleLock.lock(); try { @@ -185,29 +208,16 @@ public class MarshallingWebServiceInboundGateway extends AbstractMarshallingPayl } } - public int getPhase() { - return 0; - } private static class GatewayDelegate extends MessagingGatewaySupport { public Object sendAndReceive(Object request) { return super.sendAndReceive(request); } + public String getComponentType() { return "ws:outbound-gateway"; } } - public String getComponentName() { - return this.gatewayDelegate.getComponentName(); - } - - public String getComponentType() { - return this.gatewayDelegate.getComponentType(); - } - - public void setShouldTrack(boolean shouldTrack) { - this.gatewayDelegate.setShouldTrack(shouldTrack); - } } 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 301956f692..eb5e02a2f9 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 @@ -16,9 +16,8 @@ package org.springframework.integration.ws; -import java.util.Iterator; +import java.util.Map; -import javax.xml.namespace.QName; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.TransformerException; @@ -30,13 +29,14 @@ 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.SoapHeaderElement; import org.springframework.ws.soap.SoapMessage; import org.springframework.xml.transform.StringSource; import org.springframework.xml.transform.TransformerObjectSupport; @@ -51,11 +51,22 @@ public class SimpleWebServiceInboundGateway extends MessagingGatewaySupport impl 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); @@ -83,21 +94,9 @@ public class SimpleWebServiceInboundGateway extends MessagingGatewaySupport impl } if (request instanceof SoapMessage) { SoapMessage soapMessage = (SoapMessage) request; - SoapHeader soapHeader = soapMessage.getSoapHeader(); - if (soapHeader != null) { - Iterator attributeIter = soapHeader.getAllAttributes(); - while (attributeIter.hasNext()) { - QName name = (QName) attributeIter.next(); - builder.setHeader(name.toString(), soapHeader.getAttributeValue(name)); - } - Iterator elementIter = soapHeader.examineAllHeaderElements(); - while (elementIter.hasNext()) { - Object element = elementIter.next(); - if (element instanceof SoapHeaderElement) { - QName name = ((SoapHeaderElement) element).getName(); - builder.setHeader(name.toString(), element); - } - } + Map headers = this.headerMapper.toHeaders(soapMessage.getSoapHeader()); + if (!CollectionUtils.isEmpty(headers)) { + builder.copyHeaders(headers); } } Message replyMessage = this.sendAndReceiveMessage(builder.build()); @@ -120,17 +119,19 @@ 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()); } } - private class TransformerSupportDelegate extends TransformerObjectSupport { + + private static class TransformerSupportDelegate extends TransformerObjectSupport { void transformSourceToResult(Source source, Result result) throws TransformerException { this.transform(source, result); } } - public String getComponentType() { - return "ws:outbound-gateway"; - } } diff --git a/spring-integration-ws/template.mf b/spring-integration-ws/template.mf index f0aa5f8361..eed0b41f62 100644 --- a/spring-integration-ws/template.mf +++ b/spring-integration-ws/template.mf @@ -11,7 +11,7 @@ Import-Template: org.springframework.util;version="[3.0.3, 4.0.0)", org.springframework.oxm;version="[1.5.8.A, 3.1.0)", org.springframework.ws.*;version="[1.5.8.A, 2.0.0)", - org.springframework.xml.transform;version="[1.5.8.A, 2.0.0)", + org.springframework.xml.*;version="[1.5.8.A, 2.0.0)", org.apache.commons.logging;version="[1.1.1, 2.0.0)", org.w3c.dom.*;version="0", javax.xml.*;version="0"