Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.consol.citrus.ws.message.converter.SoapMessageConverter Maven / Gradle / Ivy
/*
* Copyright 2006-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 com.consol.citrus.ws.message.converter;
import com.consol.citrus.Citrus;
import com.consol.citrus.context.TestContext;
import com.consol.citrus.exceptions.CitrusRuntimeException;
import com.consol.citrus.message.*;
import com.consol.citrus.ws.client.WebServiceEndpointConfiguration;
import com.consol.citrus.ws.message.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.mime.Attachment;
import org.springframework.ws.soap.SoapHeader;
import org.springframework.ws.soap.SoapHeaderElement;
import org.springframework.ws.soap.axiom.AxiomSoapMessage;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.springframework.ws.transport.WebServiceConnection;
import org.springframework.ws.transport.context.TransportContext;
import org.springframework.ws.transport.context.TransportContextHolder;
import org.springframework.ws.transport.http.HttpServletConnection;
import org.springframework.xml.namespace.QNameUtils;
import org.springframework.xml.transform.StringResult;
import org.springframework.xml.transform.StringSource;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import javax.servlet.http.HttpServletRequest;
import javax.xml.namespace.QName;
import javax.xml.soap.MimeHeader;
import javax.xml.soap.MimeHeaders;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.*;
import java.util.Map.Entry;
/**
* Default converter implementation for SOAP messages. By default strips away the SOAP envelope and constructs internal message representation
* from incoming SOAP request messages. Response messages are created from internal message representation accordingly.
*
* @author Christoph Deppisch
* @since 2.0
*/
public class SoapMessageConverter implements WebServiceMessageConverter {
/** Logger */
private static Logger log = LoggerFactory.getLogger(SoapMessageConverter.class);
/** Default payload source encoding */
private String charset = Citrus.CITRUS_FILE_ENCODING;
@Override
public WebServiceMessage convertOutbound(Message internalMessage, WebServiceEndpointConfiguration endpointConfiguration, TestContext context) {
WebServiceMessage message = endpointConfiguration.getMessageFactory().createWebServiceMessage();
convertOutbound(message, internalMessage, endpointConfiguration, context);
return message;
}
@Override
public void convertOutbound(WebServiceMessage webServiceMessage, Message message, WebServiceEndpointConfiguration endpointConfiguration, TestContext context) {
org.springframework.ws.soap.SoapMessage soapRequest = ((org.springframework.ws.soap.SoapMessage)webServiceMessage);
SoapMessage soapMessage;
if (message instanceof SoapMessage) {
soapMessage = (SoapMessage) message;
} else {
soapMessage = new SoapMessage(message);
}
// Copy payload into soap-body:
TransformerFactory transformerFactory = TransformerFactory.newInstance();
try {
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(new StringSource(soapMessage.getPayload(String.class)), soapRequest.getSoapBody().getPayloadResult());
} catch (TransformerException e) {
throw new CitrusRuntimeException("Failed to write SOAP body payload", e);
}
// Copy headers into soap-header:
for (Entry headerEntry : soapMessage.getHeaders().entrySet()) {
if (MessageHeaderUtils.isSpringInternalHeader(headerEntry.getKey())) {
continue;
}
if (headerEntry.getKey().equalsIgnoreCase(SoapMessageHeaders.SOAP_ACTION)) {
soapRequest.setSoapAction(headerEntry.getValue().toString());
} else if (headerEntry.getKey().toLowerCase().startsWith(SoapMessageHeaders.HTTP_PREFIX)) {
handleOutboundMimeMessageHeader(soapRequest,
headerEntry.getKey().substring(SoapMessageHeaders.HTTP_PREFIX.length()),
headerEntry.getValue(),
endpointConfiguration.isHandleMimeHeaders());
} else if (!headerEntry.getKey().startsWith(MessageHeaders.PREFIX)) {
SoapHeaderElement headerElement;
if (QNameUtils.validateQName(headerEntry.getKey())) {
headerElement = soapRequest.getSoapHeader().addHeaderElement(QNameUtils.parseQNameString(headerEntry.getKey()));
} else {
headerElement = soapRequest.getSoapHeader().addHeaderElement(new QName("", headerEntry.getKey(), ""));
}
headerElement.setText(headerEntry.getValue().toString());
}
}
for (String headerData : soapMessage.getHeaderData()) {
try {
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new StringSource(headerData),
soapRequest.getSoapHeader().getResult());
} catch (TransformerException e) {
throw new CitrusRuntimeException("Failed to write SOAP header content", e);
}
}
if (soapMessage.isMtomEnabled() && soapMessage.getAttachments().size() > 0) {
log.debug("Converting SOAP request to XOP package");
soapRequest.convertToXopPackage();
}
for (final Attachment attachment : soapMessage.getAttachments()) {
String contentId = context.replaceDynamicContentInString(attachment.getContentId());
if (!contentId.startsWith("<")) {
contentId = "<" + contentId + ">";
}
if (log.isDebugEnabled()) {
log.debug(String.format("Adding attachment to SOAP message: '%s' ('%s')", contentId, attachment.getContentType()));
}
soapRequest.addAttachment(contentId, attachment::getInputStream, attachment.getContentType());
}
}
@Override
public SoapMessage convertInbound(WebServiceMessage message, WebServiceEndpointConfiguration endpointConfiguration, TestContext context) {
return convertInbound(message, null, endpointConfiguration);
}
@Override
public SoapMessage convertInbound(WebServiceMessage webServiceMessage, MessageContext messageContext, WebServiceEndpointConfiguration endpointConfiguration) {
try {
String payload = "";
if (endpointConfiguration.isKeepSoapEnvelope()) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
webServiceMessage.writeTo(bos);
payload = bos.toString(charset);
} else if (webServiceMessage.getPayloadSource() != null) {
StringResult payloadResult = new StringResult();
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(webServiceMessage.getPayloadSource(), payloadResult);
payload = payloadResult.toString();
}
SoapMessage message = new SoapMessage(payload);
handleInboundMessageProperties(messageContext, message);
if (webServiceMessage instanceof org.springframework.ws.soap.SoapMessage) {
handleInboundSoapMessage((org.springframework.ws.soap.SoapMessage) webServiceMessage, message, endpointConfiguration);
}
handleInboundHttpHeaders(message, endpointConfiguration);
return message;
} catch (TransformerException e) {
throw new CitrusRuntimeException("Failed to read web service message payload source", e);
} catch (IOException e) {
throw new CitrusRuntimeException("Failed to read web service message", e);
}
}
/**
* Method handles SOAP specific message information such as SOAP action headers and SOAP attachments.
*
* @param soapMessage
* @param message
* @param endpointConfiguration
*/
protected void handleInboundSoapMessage(org.springframework.ws.soap.SoapMessage soapMessage, SoapMessage message, WebServiceEndpointConfiguration endpointConfiguration) {
handleInboundNamespaces(soapMessage, message);
handleInboundSoapHeaders(soapMessage, message);
handleInboundAttachments(soapMessage, message);
if (endpointConfiguration.isHandleMimeHeaders()) {
handleInboundMimeHeaders(soapMessage, message);
}
}
private void handleInboundNamespaces(org.springframework.ws.soap.SoapMessage soapMessage, SoapMessage message) {
Source envelopeSource = soapMessage.getEnvelope().getSource();
if (envelopeSource != null && envelopeSource instanceof DOMSource) {
Node envelopeNode = ((DOMSource) envelopeSource).getNode();
NamedNodeMap attributes = envelopeNode.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node attribute = attributes.item(i);
if (StringUtils.hasText(attribute.getNamespaceURI()) && attribute.getNamespaceURI().equals("http://www.w3.org/2000/xmlns/")) {
if (StringUtils.hasText(attribute.getNodeValue()) && !attribute.getNodeValue().equals(envelopeNode.getNamespaceURI())) {
String messagePayload = message.getPayload(String.class);
if (StringUtils.hasText(messagePayload)) {
int xmlProcessingInstruction = messagePayload.indexOf("?>");
xmlProcessingInstruction = xmlProcessingInstruction > 0 ? (xmlProcessingInstruction + 2) : 0;
int rootElementEnd = messagePayload.indexOf('>', xmlProcessingInstruction);
if (rootElementEnd > 0) {
if (messagePayload.charAt(rootElementEnd - 1) == '/') {
// root element is closed immediately e.g. need to adjust root element end
rootElementEnd--;
}
String namespace = attribute.getNodeName() + "=\"" + attribute.getNodeValue() + "\"";
if (!messagePayload.contains(namespace)) {
message.setPayload(messagePayload.substring(0, rootElementEnd) + " " + namespace + messagePayload.substring(rootElementEnd));
}
}
}
}
}
}
}
}
/**
* Reads information from Http connection and adds them as Http marked headers to internal message representation.
*
* @param message
*/
protected void handleInboundHttpHeaders(SoapMessage message, WebServiceEndpointConfiguration endpointConfiguration) {
TransportContext transportContext = TransportContextHolder.getTransportContext();
if (transportContext == null) {
log.warn("Unable to get complete set of http request headers - no transport context available");
return;
}
WebServiceConnection connection = transportContext.getConnection();
if (connection instanceof HttpServletConnection) {
UrlPathHelper pathHelper = new UrlPathHelper();
HttpServletConnection servletConnection = (HttpServletConnection) connection;
HttpServletRequest httpServletRequest = servletConnection.getHttpServletRequest();
message.setHeader(SoapMessageHeaders.HTTP_REQUEST_URI, pathHelper.getRequestUri(httpServletRequest));
message.setHeader(SoapMessageHeaders.HTTP_CONTEXT_PATH, pathHelper.getContextPath(httpServletRequest));
String queryParams = pathHelper.getOriginatingQueryString(httpServletRequest);
message.setHeader(SoapMessageHeaders.HTTP_QUERY_PARAMS, queryParams != null ? queryParams : "");
message.setHeader(SoapMessageHeaders.HTTP_REQUEST_METHOD, httpServletRequest.getMethod());
if (endpointConfiguration.isHandleAttributeHeaders()) {
Enumeration attributeNames = httpServletRequest.getAttributeNames();
while (attributeNames.hasMoreElements()) {
String attributeName = attributeNames.nextElement();
Object attribute = httpServletRequest.getAttribute(attributeName);
message.setHeader(attributeName, attribute);
}
}
} else {
log.warn("Unable to get complete set of http request headers");
try {
message.setHeader(SoapMessageHeaders.HTTP_REQUEST_URI, connection.getUri());
} catch (URISyntaxException e) {
log.warn("Unable to get http request uri from http connection", e);
}
}
}
/**
* Reads all soap headers from web service message and
* adds them to message builder as normal headers. Also takes care of soap action header.
*
* @param soapMessage the web service message.
* @param message the response message builder.
*/
protected void handleInboundSoapHeaders(org.springframework.ws.soap.SoapMessage soapMessage, SoapMessage message) {
try {
SoapHeader soapHeader = soapMessage.getSoapHeader();
if (soapHeader != null) {
Iterator> iter = soapHeader.examineAllHeaderElements();
while (iter.hasNext()) {
SoapHeaderElement headerEntry = (SoapHeaderElement) iter.next();
MessageHeaderUtils.setHeader(message, headerEntry.getName().getLocalPart(), headerEntry.getText());
}
if (soapHeader.getSource() != null) {
StringResult headerData = new StringResult();
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(soapHeader.getSource(), headerData);
message.addHeaderData(headerData.toString());
}
}
if (StringUtils.hasText(soapMessage.getSoapAction())) {
if (soapMessage.getSoapAction().equals("\"\"")) {
message.setHeader(SoapMessageHeaders.SOAP_ACTION, "");
} else {
if (soapMessage.getSoapAction().startsWith("\"") && soapMessage.getSoapAction().endsWith("\"")) {
message.setHeader(SoapMessageHeaders.SOAP_ACTION,
soapMessage.getSoapAction().substring(1, soapMessage.getSoapAction().length() - 1));
} else {
message.setHeader(SoapMessageHeaders.SOAP_ACTION, soapMessage.getSoapAction());
}
}
}
} catch (TransformerException e) {
throw new CitrusRuntimeException("Failed to read SOAP header source", e);
}
}
/**
* Adds a HTTP message header to the SOAP message.
*
* @param message the SOAP request message.
* @param name the header name.
* @param value the header value.
* @param handleMimeHeaders should handle mime headers.
*/
private void handleOutboundMimeMessageHeader(org.springframework.ws.soap.SoapMessage message, String name, Object value, boolean handleMimeHeaders) {
if (!handleMimeHeaders) {
return;
}
if (message instanceof SaajSoapMessage) {
SaajSoapMessage soapMsg = (SaajSoapMessage) message;
MimeHeaders headers = soapMsg.getSaajMessage().getMimeHeaders();
headers.setHeader(name, value.toString());
} else if (message instanceof AxiomSoapMessage) {
log.warn("Unable to set mime message header '" + name + "' on AxiomSoapMessage - unsupported");
} else {
log.warn("Unsupported SOAP message implementation - unable to set mime message header '" + name + "'");
}
}
/**
* Adds mime headers to constructed response message. This can be HTTP headers in case
* of HTTP transport. Note: HTTP headers may have multiple values that are represented as
* comma delimited string value.
*
* @param soapMessage the source SOAP message.
* @param message the message build constructing the result message.
*/
protected void handleInboundMimeHeaders(org.springframework.ws.soap.SoapMessage soapMessage, SoapMessage message) {
Map mimeHeaders = new HashMap();
MimeHeaders messageMimeHeaders = null;
// to get access to mime headers we need to get implementation specific here
if (soapMessage instanceof SaajSoapMessage) {
messageMimeHeaders = ((SaajSoapMessage)soapMessage).getSaajMessage().getMimeHeaders();
} else if (soapMessage instanceof AxiomSoapMessage) {
// we do not handle axiom message implementations as it is very difficult to get access to the mime headers there
log.warn("Skip mime headers for AxiomSoapMessage - unsupported");
} else {
log.warn("Unsupported SOAP message implementation - skipping mime headers");
}
if (messageMimeHeaders != null) {
Iterator> mimeHeaderIterator = messageMimeHeaders.getAllHeaders();
while (mimeHeaderIterator.hasNext()) {
MimeHeader mimeHeader = (MimeHeader)mimeHeaderIterator.next();
// http headers can have multpile values so headers might occur several times in map
if (mimeHeaders.containsKey(mimeHeader.getName())) {
// header is already present, so concat values to a single comma delimited string
String value = mimeHeaders.get(mimeHeader.getName());
value += ", " + mimeHeader.getValue();
mimeHeaders.put(mimeHeader.getName(), value);
} else {
mimeHeaders.put(mimeHeader.getName(), mimeHeader.getValue());
}
}
for (Entry httpHeaderEntry : mimeHeaders.entrySet()) {
message.setHeader(httpHeaderEntry.getKey(), httpHeaderEntry.getValue());
}
}
}
/**
* Adds all message properties from web service message to message builder
* as normal header entries.
*
* @param messageContext the web service request message context.
* @param message the request message builder.
*/
protected void handleInboundMessageProperties(MessageContext messageContext, SoapMessage message) {
if (messageContext == null) { return; }
String[] propertyNames = messageContext.getPropertyNames();
if (propertyNames != null) {
for (String propertyName : propertyNames) {
message.setHeader(propertyName, messageContext.getProperty(propertyName));
}
}
}
/**
* Adds attachments if present in soap web service message.
*
* @param soapMessage the web service message.
* @param message the response message builder.
* @throws IOException
*/
protected void handleInboundAttachments(org.springframework.ws.soap.SoapMessage soapMessage, SoapMessage message) {
Iterator attachments = soapMessage.getAttachments();
while (attachments.hasNext()) {
Attachment attachment = attachments.next();
SoapAttachment soapAttachment = SoapAttachment.from(attachment);
if (log.isDebugEnabled()) {
log.debug(String.format("SOAP message contains attachment with contentId '%s'", soapAttachment.getContentId()));
}
message.addAttachment(soapAttachment);
}
}
/**
* Gets the charset.
*
* @return
*/
public String getCharset() {
return charset;
}
/**
* Sets the charset.
*
* @param charset
*/
public void setCharset(String charset) {
this.charset = charset;
}
}