/*
* soapUI, copyright (C) 2004-2011 eviware.com
*
* soapUI is free software; you can redistribute it and/or modify it under the
* terms of version 2.1 of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details at gnu.org.
*/
package com.eviware.soapui.impl.wsdl.support.soap;
import java.util.List;
import javax.wsdl.BindingOperation;
import javax.wsdl.Message;
import javax.wsdl.Part;
import javax.xml.namespace.QName;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.wsdl.WsdlOperation;
import com.eviware.soapui.impl.wsdl.mock.DispatchException;
import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils;
import com.eviware.soapui.model.iface.Attachment;
import com.eviware.soapui.support.StringUtils;
import com.eviware.soapui.support.types.StringToStringsMap;
import com.eviware.soapui.support.xml.XmlUtils;
/**
* SOAP-related utility-methods..
*
* @author ole.matzura
*/
public class SoapUtils
{
public static boolean isSoapFault( String responseContent, SoapVersion soapVersion ) throws XmlException
{
if( StringUtils.isNullOrEmpty( responseContent ) )
return false;
// check manually before resource intensive xpath
if( responseContent.indexOf( ":Fault" ) > 0 || responseContent.indexOf( "<Fault" ) > 0 )
{
// XmlObject xml = XmlObject.Factory.parse( responseContent );
XmlObject xml = XmlUtils.createXmlObject( responseContent );
XmlObject[] paths = xml.selectPath( "declare namespace env='" + soapVersion.getEnvelopeNamespace() + "';"
+ "//env:Fault" );
if( paths.length > 0 )
return true;
}
return false;
}
/**
* Init soapversion from content-type header.. should envelope be checked
* and/or override?
*
* @param xmlObject
*/
public static SoapVersion deduceSoapVersion( String contentType, XmlObject xmlObject )
{
if( xmlObject != null )
{
Element elm = ( ( Document )( xmlObject.getDomNode() ) ).getDocumentElement();
if( elm.getLocalName().equals( "Envelope" ) )
{
if( elm.getNamespaceURI().equals( SoapVersion.Soap11.getEnvelopeNamespace() ) )
return SoapVersion.Soap11;
else if( elm.getNamespaceURI().equals( SoapVersion.Soap12.getEnvelopeNamespace() ) )
return SoapVersion.Soap12;
}
}
SoapVersion soapVersion = null;
if( StringUtils.isNullOrEmpty( contentType ) )
return null;
soapVersion = contentType.startsWith( SoapVersion.Soap11.getContentType() ) ? SoapVersion.Soap11 : null;
soapVersion = soapVersion == null && contentType.startsWith( SoapVersion.Soap12.getContentType() ) ? SoapVersion.Soap12
: soapVersion;
if( soapVersion == null && contentType.startsWith( "application/xop+xml" ) )
{
if( contentType.indexOf( "type=\"" + SoapVersion.Soap11.getContentType() + "\"" ) > 0 )
soapVersion = SoapVersion.Soap11;
else if( contentType.indexOf( "type=\"" + SoapVersion.Soap12.getContentType() + "\"" ) > 0 )
soapVersion = SoapVersion.Soap12;
}
return soapVersion;
}
public static String getSoapAction( SoapVersion soapVersion, StringToStringsMap headers )
{
String soapAction = null;
String contentType = headers.get( "Content-Type", "" );
if( soapVersion == SoapVersion.Soap11 )
{
soapAction = headers.get( "SOAPAction", "" );
}
else if( soapVersion == SoapVersion.Soap12 )
{
int ix = contentType.indexOf( "action=" );
if( ix > 0 )
{
int endIx = contentType.indexOf( ';', ix );
soapAction = endIx == -1 ? contentType.substring( ix + 7 ) : contentType.substring( ix + 7, endIx );
}
}
soapAction = StringUtils.unquote( soapAction );
return soapAction;
}
public static XmlObject getBodyElement( XmlObject messageObject, SoapVersion soapVersion ) throws XmlException
{
XmlObject[] envelope = messageObject.selectChildren( soapVersion.getEnvelopeQName() );
if( envelope.length != 1 )
throw new XmlException( "Missing/Invalid SOAP Envelope, expecting [" + soapVersion.getEnvelopeQName() + "]" );
XmlObject[] body = envelope[0].selectChildren( soapVersion.getBodyQName() );
if( body.length != 1 )
throw new XmlException( "Missing/Invalid SOAP Body, expecting [" + soapVersion.getBodyQName() + "]" );
return body[0];
}
public static XmlObject getHeaderElement( XmlObject messageObject, SoapVersion soapVersion, boolean create )
throws XmlException
{
XmlObject[] envelope = messageObject.selectChildren( soapVersion.getEnvelopeQName() );
if( envelope.length != 1 )
throw new XmlException( "Missing/Invalid SOAP Envelope, expecting [" + soapVersion.getEnvelopeQName() + "]" );
QName headerQName = soapVersion.getHeaderQName();
XmlObject[] header = envelope[0].selectChildren( headerQName );
if( header.length == 0 && create )
{
Element elm = ( Element )envelope[0].getDomNode();
Element headerElement = elm.getOwnerDocument().createElementNS( headerQName.getNamespaceURI(),
headerQName.getLocalPart() );
elm.insertBefore( headerElement, elm.getFirstChild() );
header = envelope[0].selectChildren( headerQName );
}
return header.length == 0 ? null : header[0];
}
public static XmlObject getContentElement( XmlObject messageObject, SoapVersion soapVersion ) throws XmlException
{
if( messageObject == null )
return null;
XmlObject bodyElement = SoapUtils.getBodyElement( messageObject, soapVersion );
if( bodyElement != null )
{
XmlCursor cursor = bodyElement.newCursor();
try
{
if( cursor.toFirstChild() )
{
while( !cursor.isContainer() )
cursor.toNextSibling();
if( cursor.isContainer() )
{
return cursor.getObject();
}
}
}
catch( Exception e )
{
SoapUI.logError( e );
}
finally
{
cursor.dispose();
}
}
return null;
}
@SuppressWarnings( "unchecked" )
public static WsdlOperation findOperationForRequest( SoapVersion soapVersion, String soapAction,
XmlObject requestContent, List<WsdlOperation> operations, boolean requireSoapVersionMatch,
boolean requireSoapActionMatch, Attachment[] attachments ) throws Exception
{
XmlObject contentElm = getContentElement( requestContent, soapVersion );
if( contentElm == null )
{
for( WsdlOperation operation : operations )
{
if( operation.getAction().equals( soapAction )
&& operation.getBindingOperation().getOperation().getInput().getMessage().getParts().size() == 0 )
{
return operation;
}
}
return null;
}
QName contentQName = XmlUtils.getQName( contentElm.getDomNode() );
NodeList contentChildNodes = null;
for( int c = 0; c < operations.size(); c++ )
{
WsdlOperation wsdlOperation = operations.get( c );
String action = wsdlOperation.getAction();
// matches soapAction?
if( !requireSoapActionMatch
|| ( ( soapAction == null && wsdlOperation.getAction() == null ) || ( action != null && action
.equals( soapAction ) ) ) )
{
QName qname = wsdlOperation.getRequestBodyElementQName();
if( !contentQName.equals( qname ) )
continue;
SoapVersion ifaceSoapVersion = wsdlOperation.getInterface().getSoapVersion();
if( requireSoapVersionMatch && ifaceSoapVersion != soapVersion )
{
continue;
}
// check content
if( wsdlOperation.getStyle().equals( WsdlOperation.STYLE_DOCUMENT ) )
{
// check that all attachments match
BindingOperation bindingOperation = wsdlOperation.getBindingOperation();
Message message = bindingOperation.getOperation().getInput().getMessage();
List<Part> parts = message.getOrderedParts( null );
for( int x = 0; x < parts.size(); x++ )
{
// check for attachment part
if( WsdlUtils.isAttachmentInputPart( parts.get( x ), bindingOperation ) )
{
for( Attachment attachment : attachments )
{
if( attachment.getPart().equals( parts.get( x ).getName() ) )
{
parts.remove( x );
x-- ;
}
}
}
else
{
parts.remove( x );
x-- ;
}
}
// matches!
if( parts.isEmpty() )
{
return wsdlOperation;
}
}
else if( wsdlOperation.getStyle().equals( WsdlOperation.STYLE_RPC ) )
{
BindingOperation bindingOperation = wsdlOperation.getBindingOperation();
Message message = bindingOperation.getOperation().getInput().getMessage();
List<Part> parts = message.getOrderedParts( null );
if( contentChildNodes == null )
contentChildNodes = XmlUtils.getChildElements( ( Element )contentElm.getDomNode() );
int i = 0;
if( parts.size() > 0 )
{
for( int x = 0; x < parts.size(); x++ )
{
if( WsdlUtils.isAttachmentInputPart( parts.get( x ), bindingOperation ) )
{
for( Attachment attachment : attachments )
{
if( attachment.getPart().equals( parts.get( x ).getName() ) )
{
parts.remove( x );
x-- ;
}
}
}
// ignore header parts for now..
if( x >= 0 && WsdlUtils.isHeaderInputPart( parts.get( x ), message, bindingOperation ) )
{
parts.remove( x );
x-- ;
}
}
for( ; i < contentChildNodes.getLength() && !parts.isEmpty(); i++ )
{
Node item = contentChildNodes.item( i );
if( item.getNodeType() != Node.ELEMENT_NODE )
continue;
int j = 0;
while( ( j < parts.size() ) && ( !item.getNodeName().equals( parts.get( j ).getName() ) ) )
{
Part part = parts.get( j );
if( part.getElementName() != null )
{
QName qn = part.getElementName();
if( item.getLocalName().equals( qn.getLocalPart() )
&& item.getNamespaceURI().equals( qn.getNamespaceURI() ) )
break;
}
else
{
if( item.getNodeName().equals( parts.get( j ).getName() ) )
break;
}
j++ ;
}
if( j == parts.size() )
break;
parts.remove( j );
}
}
// match?
if( i == contentChildNodes.getLength() && parts.isEmpty() )
{
return wsdlOperation;
}
}
}
}
throw new DispatchException( "Missing operation for soapAction [" + soapAction + "] and body element ["
+ contentQName + "] with SOAP Version [" + soapVersion + "]" );
}
@SuppressWarnings( "unchecked" )
public static WsdlOperation findOperationForResponse( SoapVersion soapVersion, String soapAction,
XmlObject responseContent, List<WsdlOperation> operations, boolean requireSoapVersionMatch,
boolean requireSoapActionMatch ) throws Exception
{
XmlObject contentElm = getContentElement( responseContent, soapVersion );
if( contentElm == null )
return null;
QName contentQName = XmlUtils.getQName( contentElm.getDomNode() );
NodeList contentChildNodes = null;
for( int c = 0; c < operations.size(); c++ )
{
WsdlOperation wsdlOperation = operations.get( c );
String action = wsdlOperation.getAction();
// matches soapAction?
if( !requireSoapActionMatch
|| ( ( soapAction == null && wsdlOperation.getAction() == null ) || ( action != null && action
.equals( soapAction ) ) ) )
{
QName qname = wsdlOperation.getResponseBodyElementQName();
if( !contentQName.equals( qname ) )
continue;
SoapVersion ifaceSoapVersion = wsdlOperation.getInterface().getSoapVersion();
if( requireSoapVersionMatch && ifaceSoapVersion != soapVersion )
{
continue;
}
// check content
if( wsdlOperation.getStyle().equals( WsdlOperation.STYLE_DOCUMENT ) )
{
// matches!
return wsdlOperation;
}
else if( wsdlOperation.getStyle().equals( WsdlOperation.STYLE_RPC ) )
{
BindingOperation bindingOperation = wsdlOperation.getBindingOperation();
Message message = bindingOperation.getOperation().getOutput().getMessage();
List<Part> parts = message.getOrderedParts( null );
if( contentChildNodes == null )
contentChildNodes = XmlUtils.getChildElements( ( Element )contentElm.getDomNode() );
int i = 0;
if( parts.size() > 0 )
{
for( int x = 0; x < parts.size(); x++ )
{
if( WsdlUtils.isAttachmentOutputPart( parts.get( x ), bindingOperation )
|| WsdlUtils.isHeaderOutputPart( parts.get( x ), message, bindingOperation ) )
{
parts.remove( x );
x-- ;
}
}
for( ; i < contentChildNodes.getLength() && !parts.isEmpty(); i++ )
{
Node item = contentChildNodes.item( i );
if( item.getNodeType() != Node.ELEMENT_NODE )
continue;
int j = 0;
while( ( j < parts.size() ) && ( !item.getNodeName().equals( parts.get( j ).getName() ) ) )
{
Part part = parts.get( j );
if( part.getElementName() != null )
{
QName qn = part.getElementName();
if( item.getLocalName().equals( qn.getLocalPart() )
&& item.getNamespaceURI().equals( qn.getNamespaceURI() ) )
break;
}
else
{
if( item.getNodeName().equals( parts.get( j ).getName() ) )
break;
}
j++ ;
}
if( j == parts.size() )
break;
parts.remove( j );
}
}
// match?
if( i == contentChildNodes.getLength() && parts.isEmpty() )
{
return wsdlOperation;
}
}
}
}
throw new DispatchException( "Missing response operation for soapAction [" + soapAction + "] and body element ["
+ contentQName + "] with SOAP Version [" + soapVersion + "]" );
}
public static String removeEmptySoapHeaders( String content, SoapVersion soapVersion ) throws XmlException
{
// XmlObject xmlObject = XmlObject.Factory.parse( content );
XmlObject xmlObject = XmlUtils.createXmlObject( content );
XmlObject[] selectPath = xmlObject.selectPath( "declare namespace soap='" + soapVersion.getEnvelopeNamespace()
+ "';/soap:Envelope/soap:Header" );
if( selectPath.length > 0 )
{
Node domNode = selectPath[0].getDomNode();
if( !domNode.hasChildNodes() && !domNode.hasAttributes() )
{
domNode.getParentNode().removeChild( domNode );
return xmlObject.xmlText();
}
}
return content;
}
public static SoapVersion deduceSoapVersion( String requestContentType, String requestContent )
{
try
{
// return deduceSoapVersion( requestContentType,
// XmlObject.Factory.parse( requestContent ) );
return deduceSoapVersion( requestContentType, XmlUtils.createXmlObject( requestContent ) );
}
catch( XmlException e )
{
return deduceSoapVersion( requestContentType, ( XmlObject )null );
}
}
public static String transferSoapHeaders( String requestContent, String newRequest, SoapVersion soapVersion )
{
try
{
// XmlObject source = XmlObject.Factory.parse( requestContent );
XmlObject source = XmlUtils.createXmlObject( requestContent );
String headerXPath = "declare namespace ns='" + soapVersion.getEnvelopeNamespace() + "'; //ns:Header";
XmlObject[] header = source.selectPath( headerXPath );
if( header.length == 1 )
{
Element headerElm = ( Element )header[0].getDomNode();
NodeList childNodes = headerElm.getChildNodes();
if( childNodes.getLength() > 0 )
{
// XmlObject dest = XmlObject.Factory.parse( newRequest );
XmlObject dest = XmlUtils.createXmlObject( newRequest );
header = dest.selectPath( headerXPath );
Element destElm = null;
if( header.length == 0 )
{
Element docElm = ( ( Document )dest.getDomNode() ).getDocumentElement();
destElm = ( Element )docElm.insertBefore(
docElm.getOwnerDocument().createElementNS( soapVersion.getEnvelopeNamespace(),
docElm.getPrefix() + ":Header" ),
XmlUtils.getFirstChildElementNS( docElm, soapVersion.getBodyQName() ) );
}
else
{
destElm = ( Element )header[0].getDomNode();
}
for( int c = 0; c < childNodes.getLength(); c++ )
{
Node childNode = childNodes.item( c );
if( childNode.getNodeType() == Node.ELEMENT_NODE )
{
if( XmlUtils.getFirstChildElementNS( destElm, childNode.getNamespaceURI(),
childNode.getLocalName() ) != null )
continue;
destElm.appendChild( destElm.getOwnerDocument().importNode( childNode, true ) );
}
}
return dest.xmlText();
}
}
}
catch( XmlException e )
{
SoapUI.logError( e );
}
return newRequest;
}
}