View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2008 eviware.com 
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of version 2.1 of the GNU Lesser General Public License as published by 
6    *  the Free Software Foundation.
7    *
8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
10   *  See the GNU Lesser General Public License for more details at gnu.org.
11   */
12  
13  package com.eviware.soapui.impl.wsdl.support.soap;
14  
15  import java.util.List;
16  
17  import javax.wsdl.BindingOperation;
18  import javax.wsdl.Message;
19  import javax.wsdl.Part;
20  import javax.xml.namespace.QName;
21  
22  import org.apache.xmlbeans.XmlCursor;
23  import org.apache.xmlbeans.XmlException;
24  import org.apache.xmlbeans.XmlObject;
25  import org.w3c.dom.Document;
26  import org.w3c.dom.Element;
27  import org.w3c.dom.Node;
28  import org.w3c.dom.NodeList;
29  
30  import com.eviware.soapui.SoapUI;
31  import com.eviware.soapui.impl.wsdl.WsdlOperation;
32  import com.eviware.soapui.impl.wsdl.mock.DispatchException;
33  import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils;
34  import com.eviware.soapui.support.StringUtils;
35  import com.eviware.soapui.support.types.StringToStringMap;
36  import com.eviware.soapui.support.xml.XmlUtils;
37  
38  /***
39   * SOAP-related utility-methods..
40   * 
41   * @author ole.matzura
42   */
43  
44  public class SoapUtils
45  {
46  	public static boolean isSoapFault( String responseContent, SoapVersion soapVersion ) throws XmlException
47  	{
48  		if( StringUtils.isNullOrEmpty( responseContent ))
49  			return false;
50  		
51  		// check manually before resource intensive xpath
52  		if( responseContent.indexOf( ":Fault" ) > 0 || responseContent.indexOf( "<Fault" ) > 0 )
53  		{
54  		   XmlObject xml = XmlObject.Factory.parse( responseContent );
55  		   XmlObject[] paths = xml.selectPath( "declare namespace env='" + soapVersion.getEnvelopeNamespace() + "';" + 
56  		         "//env:Fault");
57  		   if( paths.length > 0 )
58  		      return true;
59  		}
60  		
61  		return false;
62  	}
63  
64  	/***
65  	 * Init soapversion from content-type header.. should envelope be checked and/or override? 
66  	 * @param xmlObject 
67  	 */
68  	
69  	public static SoapVersion deduceSoapVersion( String contentType, XmlObject xmlObject )
70  	{
71  		if( xmlObject != null )
72  		{
73  			Element elm = ((Document)(xmlObject.getDomNode())).getDocumentElement();
74  			if( elm.getLocalName().equals("Envelope"))
75  			{
76  				if( elm.getNamespaceURI().equals( SoapVersion.Soap11.getEnvelopeNamespace()))
77  					return SoapVersion.Soap11;
78  				else if( elm.getNamespaceURI().equals( SoapVersion.Soap12.getEnvelopeNamespace()))
79  					return SoapVersion.Soap12;
80  			}
81  		}
82  		
83  		SoapVersion soapVersion = null;
84  		
85  		if( StringUtils.isNullOrEmpty( contentType ) )
86  			return null;
87  		
88  		soapVersion = contentType.startsWith( SoapVersion.Soap11.getContentType() ) ? SoapVersion.Soap11 : null;
89  		soapVersion = soapVersion == null && contentType.startsWith( SoapVersion.Soap12.getContentType() ) ? SoapVersion.Soap12 : soapVersion;
90  		if( soapVersion == null && contentType.startsWith( "application/xop+xml" ))
91  		{
92  			if( contentType.indexOf(  "type=\"" + SoapVersion.Soap11.getContentType() + "\"" ) > 0 )
93  				soapVersion = SoapVersion.Soap11;
94  			else if( contentType.indexOf(  "type=\"" + SoapVersion.Soap12.getContentType() + "\"" ) > 0 )
95  				soapVersion = SoapVersion.Soap12;
96  		}
97  	
98  		return soapVersion;
99  	}
100 
101 	public static String getSoapAction( SoapVersion soapVersion, StringToStringMap headers )
102 	{
103 		String soapAction = null;
104 		String contentType = headers.get( "Content-Type" );
105 		
106 		if( soapVersion == SoapVersion.Soap11 )
107 		{
108 			soapAction = headers.get( "SOAPAction" );
109 		}
110 		else if( soapVersion == SoapVersion.Soap12 )
111 		{
112 			int ix = contentType.indexOf( "action=" );
113 			if( ix > 0 )
114 			{
115 				int endIx = contentType.indexOf( ';', ix );
116 				soapAction = endIx == -1 ? contentType.substring( ix + 7 ) : contentType.substring( ix + 7, endIx );
117 			}
118 		}
119 		
120 		soapAction = StringUtils.unquote( soapAction );
121 		
122 		return soapAction;
123 	}
124 
125 	public static XmlObject getBodyElement(XmlObject messageObject, SoapVersion soapVersion ) throws XmlException
126 	{
127 		XmlObject[] envelope = messageObject.selectChildren( soapVersion.getEnvelopeQName() );
128 		if( envelope.length != 1 )
129 		    throw new XmlException( "Missing/Invalid SOAP Envelope, expecting [" + soapVersion.getEnvelopeQName() + "]" );
130 		
131 		XmlObject[] body = envelope[0].selectChildren( soapVersion.getBodyQName() );
132 		if( body.length != 1 )
133 		    throw new XmlException( "Missing/Invalid SOAP Body, expecting [" + soapVersion.getBodyQName() + "]" );
134 		
135 		return body[0];
136 	}
137 
138 	public static XmlObject getHeaderElement(XmlObject messageObject, SoapVersion soapVersion, boolean create ) throws XmlException
139 	{
140 		XmlObject[] envelope = messageObject.selectChildren( soapVersion.getEnvelopeQName() );
141 		if( envelope.length != 1 )
142 		    throw new XmlException( "Missing/Invalid SOAP Envelope, expecting [" + soapVersion.getEnvelopeQName() + "]" );
143 		
144 		QName headerQName = soapVersion.getHeaderQName();
145 		XmlObject[] header = envelope[0].selectChildren( headerQName );
146 		if( header.length == 0 && create )
147 		{
148          Element elm = (Element) envelope[0].getDomNode();
149          Element headerElement = elm.getOwnerDocument().createElementNS(
150          		headerQName.getNamespaceURI(), headerQName.getLocalPart() );
151          
152 			elm.insertBefore(headerElement, elm.getFirstChild());
153          
154          header = envelope[0].selectChildren( headerQName );
155 		}
156 		
157 		return header.length == 0 ? null : header[0];
158 	}
159 	
160 	
161 	public static XmlObject getContentElement( XmlObject messageObject, SoapVersion soapVersion ) throws XmlException
162 	{
163 		XmlObject bodyElement = SoapUtils.getBodyElement(messageObject, soapVersion);
164 		if( bodyElement != null )
165 		{
166 			XmlCursor cursor = bodyElement.newCursor();
167 			
168 			try
169 			{
170 				if( cursor.toFirstChild() )
171 				{
172 					while( !cursor.isContainer() )
173 						cursor.toNextSibling();
174 
175 					if( cursor.isContainer() )
176 					{
177 						return cursor.getObject();
178 					}
179 				}
180 			}
181 			catch( Exception e )
182 			{
183 				SoapUI.logError( e );
184 			}
185 			finally
186 			{
187 				cursor.dispose();
188 			}
189 		}
190 		
191 		return null;
192 	}
193 
194 	@SuppressWarnings("unchecked")
195 	public static WsdlOperation findOperationForRequest( SoapVersion soapVersion, String soapAction, XmlObject requestContent, 
196 				List<WsdlOperation> operations, boolean requireSoapVersionMatch, boolean requireSoapActionMatch ) throws XmlException, DispatchException, Exception
197 	{
198 		XmlObject contentElm = getContentElement( requestContent, soapVersion );
199 		if( contentElm == null )
200 			throw new DispatchException( "Missing content element in body" );
201 	
202 		QName contentQName = XmlUtils.getQName( contentElm.getDomNode() );
203 		NodeList contentChildNodes = null;
204 	
205 		for( int c = 0; c < operations.size(); c++ )
206 		{
207 			WsdlOperation wsdlOperation = operations.get( c );
208 			String action = wsdlOperation.getAction();
209 
210 			// matches soapAction?
211 			if( !requireSoapActionMatch || (
212 						( soapAction == null && wsdlOperation.getAction() == null )	|| 
213 						( action != null && action.equals( soapAction )) 
214 						))
215 			{
216 				QName qname = wsdlOperation.getRequestBodyElementQName();
217 
218 				if( !contentQName.equals( qname ) )
219 					continue;
220 				
221 				SoapVersion ifaceSoapVersion = wsdlOperation.getInterface().getSoapVersion();
222 				
223 				if( requireSoapVersionMatch && ifaceSoapVersion != soapVersion ) 
224 				{
225 					continue;
226 				}
227 
228 				// check content
229 				if( wsdlOperation.getStyle().equals( WsdlOperation.STYLE_DOCUMENT ) )
230 				{
231 					// matches!
232 					return wsdlOperation;
233 				}
234 				else if( wsdlOperation.getStyle().equals( WsdlOperation.STYLE_RPC ) )
235 				{
236 					BindingOperation bindingOperation = wsdlOperation.getBindingOperation();
237 					Message message = bindingOperation.getOperation().getInput().getMessage();
238 					List<Part> parts = message.getOrderedParts( null );
239 
240 					if( contentChildNodes == null )
241 						contentChildNodes = XmlUtils.getChildElements( ( Element ) contentElm.getDomNode() );
242 
243 					int i = 0;
244 
245 					if( parts.size() > 0 )
246 					{
247 						for( int x = 0; x < parts.size(); x++ )
248 						{
249 							if( WsdlUtils.isAttachmentInputPart( parts.get( x ), bindingOperation ) ||
250 								 WsdlUtils.isHeaderInputPart( parts.get( x ), message, bindingOperation ))
251 							{
252 								parts.remove( x );
253 								x--;
254 							}
255 						}
256 
257 						for( ; i < contentChildNodes.getLength() && !parts.isEmpty(); i++ )
258 						{
259 							Node item = contentChildNodes.item( i );
260 							if( item.getNodeType() != Node.ELEMENT_NODE )
261 								continue;
262 
263 							int j = 0;
264 							while( ( j < parts.size() ) && ( !item.getNodeName().equals( parts.get( j ).getName() ) ) )
265 							{
266 								j++;
267 							}
268 
269 							if( j == parts.size() )
270 								break;
271 
272 							parts.remove( j );
273 						}
274 					}
275 
276 					// match?
277 					if( i == contentChildNodes.getLength() && parts.isEmpty() )
278 					{
279 						return wsdlOperation;
280 					}
281 				}
282 			}
283 		}
284 	
285 		throw new DispatchException( "Missing operation for soapAction [" + soapAction + "] and body element ["
286 					+ contentQName + "] with SOAP Version [" + soapVersion + "]" );
287 	}
288 	
289 	public static String removeEmptySoapHeaders( String content, SoapVersion soapVersion ) throws XmlException
290 	{
291 		XmlObject xmlObject = XmlObject.Factory.parse( content );
292 		XmlObject[] selectPath = xmlObject.selectPath( "declare namespace soap='" + soapVersion.getEnvelopeNamespace() + "';/soap:Envelope/soap:Header" );
293 		if( selectPath.length > 0 )
294 		{
295 			Node domNode = selectPath[0].getDomNode();
296 			if( !domNode.hasChildNodes() && !domNode.hasAttributes())
297 			{
298 				domNode.getParentNode().removeChild( domNode );
299 				return xmlObject.xmlText();
300 			}
301 		}
302 		
303 		return content;
304 	}
305 
306 	public static SoapVersion deduceSoapVersion(String requestContentType, String requestContent)
307 	{
308 		try
309 		{
310 			return deduceSoapVersion(requestContentType, XmlObject.Factory.parse(requestContent));
311 		}
312 		catch (XmlException e)
313 		{
314 			return deduceSoapVersion(requestContentType,(XmlObject)null);
315 		}
316 	}
317 }