1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.impl.wsdl.support.soap;
14
15 import com.eviware.soapui.SoapUI;
16 import com.eviware.soapui.impl.wsdl.WsdlOperation;
17 import com.eviware.soapui.impl.wsdl.mock.DispatchException;
18 import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils;
19 import com.eviware.soapui.support.StringUtils;
20 import com.eviware.soapui.support.types.StringToStringMap;
21 import com.eviware.soapui.support.xml.XmlUtils;
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 javax.wsdl.BindingOperation;
31 import javax.wsdl.Message;
32 import javax.wsdl.Part;
33 import javax.xml.namespace.QName;
34 import java.util.List;
35
36 /***
37 * SOAP-related utility-methods..
38 *
39 * @author ole.matzura
40 */
41
42 public class SoapUtils
43 {
44 public static boolean isSoapFault( String responseContent, SoapVersion soapVersion ) throws XmlException
45 {
46 if( StringUtils.isNullOrEmpty( responseContent ))
47 return false;
48
49
50 if( responseContent.indexOf( ":Fault" ) > 0 || responseContent.indexOf( "<Fault" ) > 0 )
51 {
52 XmlObject xml = XmlObject.Factory.parse( responseContent );
53 XmlObject[] paths = xml.selectPath( "declare namespace env='" + soapVersion.getEnvelopeNamespace() + "';" +
54 "//env:Fault");
55 if( paths.length > 0 )
56 return true;
57 }
58
59 return false;
60 }
61
62 /***
63 * Init soapversion from content-type header.. should envelope be checked and/or override?
64 * @param xmlObject
65 */
66
67 public static SoapVersion deduceSoapVersion( String contentType, XmlObject xmlObject )
68 {
69 if( xmlObject != null )
70 {
71 Element elm = ((Document)(xmlObject.getDomNode())).getDocumentElement();
72 if( elm.getLocalName().equals("Envelope"))
73 {
74 if( elm.getNamespaceURI().equals( SoapVersion.Soap11.getEnvelopeNamespace()))
75 return SoapVersion.Soap11;
76 else if( elm.getNamespaceURI().equals( SoapVersion.Soap12.getEnvelopeNamespace()))
77 return SoapVersion.Soap12;
78 }
79 }
80
81 SoapVersion soapVersion = null;
82
83 if( StringUtils.isNullOrEmpty( contentType ) )
84 return null;
85
86 soapVersion = contentType.startsWith( SoapVersion.Soap11.getContentType() ) ? SoapVersion.Soap11 : null;
87 soapVersion = soapVersion == null && contentType.startsWith( SoapVersion.Soap12.getContentType() ) ? SoapVersion.Soap12 : soapVersion;
88 if( soapVersion == null && contentType.startsWith( "application/xop+xml" ))
89 {
90 if( contentType.indexOf( "type=\"" + SoapVersion.Soap11.getContentType() + "\"" ) > 0 )
91 soapVersion = SoapVersion.Soap11;
92 else if( contentType.indexOf( "type=\"" + SoapVersion.Soap12.getContentType() + "\"" ) > 0 )
93 soapVersion = SoapVersion.Soap12;
94 }
95
96 return soapVersion;
97 }
98
99 public static String getSoapAction( SoapVersion soapVersion, StringToStringMap headers )
100 {
101 String soapAction = null;
102 String contentType = headers.get( "Content-Type" );
103
104 if( soapVersion == SoapVersion.Soap11 )
105 {
106 soapAction = headers.get( "SOAPAction" );
107 }
108 else if( soapVersion == SoapVersion.Soap12 )
109 {
110 int ix = contentType.indexOf( "action=" );
111 if( ix > 0 )
112 {
113 int endIx = contentType.indexOf( ';', ix );
114 soapAction = endIx == -1 ? contentType.substring( ix + 7 ) : contentType.substring( ix + 7, endIx );
115 }
116 }
117
118 soapAction = StringUtils.unquote( soapAction );
119
120 return soapAction;
121 }
122
123 public static XmlObject getBodyElement(XmlObject messageObject, SoapVersion soapVersion ) throws XmlException
124 {
125 XmlObject[] envelope = messageObject.selectChildren( soapVersion.getEnvelopeQName() );
126 if( envelope.length != 1 )
127 throw new XmlException( "Missing/Invalid SOAP Envelope, expecting [" + soapVersion.getEnvelopeQName() + "]" );
128
129 XmlObject[] body = envelope[0].selectChildren( soapVersion.getBodyQName() );
130 if( body.length != 1 )
131 throw new XmlException( "Missing/Invalid SOAP Body, expecting [" + soapVersion.getBodyQName() + "]" );
132
133 return body[0];
134 }
135
136 public static XmlObject getHeaderElement(XmlObject messageObject, SoapVersion soapVersion, boolean create ) throws XmlException
137 {
138 XmlObject[] envelope = messageObject.selectChildren( soapVersion.getEnvelopeQName() );
139 if( envelope.length != 1 )
140 throw new XmlException( "Missing/Invalid SOAP Envelope, expecting [" + soapVersion.getEnvelopeQName() + "]" );
141
142 QName headerQName = soapVersion.getHeaderQName();
143 XmlObject[] header = envelope[0].selectChildren( headerQName );
144 if( header.length == 0 && create )
145 {
146 Element elm = (Element) envelope[0].getDomNode();
147 Element headerElement = elm.getOwnerDocument().createElementNS(
148 headerQName.getNamespaceURI(), headerQName.getLocalPart() );
149
150 elm.insertBefore(headerElement, elm.getFirstChild());
151
152 header = envelope[0].selectChildren( headerQName );
153 }
154
155 return header.length == 0 ? null : header[0];
156 }
157
158
159 public static XmlObject getContentElement( XmlObject messageObject, SoapVersion soapVersion ) throws XmlException
160 {
161 XmlObject bodyElement = SoapUtils.getBodyElement(messageObject, soapVersion);
162 if( bodyElement != null )
163 {
164 XmlCursor cursor = bodyElement.newCursor();
165
166 try
167 {
168 if( cursor.toFirstChild() )
169 {
170 while( !cursor.isContainer() )
171 cursor.toNextSibling();
172
173 if( cursor.isContainer() )
174 {
175 return cursor.getObject();
176 }
177 }
178 }
179 catch( Exception e )
180 {
181 SoapUI.logError( e );
182 }
183 finally
184 {
185 cursor.dispose();
186 }
187 }
188
189 return null;
190 }
191
192 @SuppressWarnings("unchecked")
193 public static WsdlOperation findOperationForRequest( SoapVersion soapVersion, String soapAction, XmlObject requestContent,
194 List<WsdlOperation> operations, boolean requireSoapVersionMatch, boolean requireSoapActionMatch ) throws Exception
195 {
196 XmlObject contentElm = getContentElement( requestContent, soapVersion );
197 if( contentElm == null )
198 throw new DispatchException( "Missing content element in body" );
199
200 QName contentQName = XmlUtils.getQName( contentElm.getDomNode() );
201 NodeList contentChildNodes = null;
202
203 for( int c = 0; c < operations.size(); c++ )
204 {
205 WsdlOperation wsdlOperation = operations.get( c );
206 String action = wsdlOperation.getAction();
207
208
209 if( !requireSoapActionMatch || (
210 ( soapAction == null && wsdlOperation.getAction() == null ) ||
211 ( action != null && action.equals( soapAction ))
212 ))
213 {
214 QName qname = wsdlOperation.getRequestBodyElementQName();
215
216 if( !contentQName.equals( qname ) )
217 continue;
218
219 SoapVersion ifaceSoapVersion = wsdlOperation.getInterface().getSoapVersion();
220
221 if( requireSoapVersionMatch && ifaceSoapVersion != soapVersion )
222 {
223 continue;
224 }
225
226
227 if( wsdlOperation.getStyle().equals( WsdlOperation.STYLE_DOCUMENT ) )
228 {
229
230 return wsdlOperation;
231 }
232 else if( wsdlOperation.getStyle().equals( WsdlOperation.STYLE_RPC ) )
233 {
234 BindingOperation bindingOperation = wsdlOperation.getBindingOperation();
235 Message message = bindingOperation.getOperation().getInput().getMessage();
236 List<Part> parts = message.getOrderedParts( null );
237
238 if( contentChildNodes == null )
239 contentChildNodes = XmlUtils.getChildElements( ( Element ) contentElm.getDomNode() );
240
241 int i = 0;
242
243 if( parts.size() > 0 )
244 {
245 for( int x = 0; x < parts.size(); x++ )
246 {
247 if( WsdlUtils.isAttachmentInputPart( parts.get( x ), bindingOperation ) ||
248 WsdlUtils.isHeaderInputPart( parts.get( x ), message, bindingOperation ))
249 {
250 parts.remove( x );
251 x--;
252 }
253 }
254
255 for( ; i < contentChildNodes.getLength() && !parts.isEmpty(); i++ )
256 {
257 Node item = contentChildNodes.item( i );
258 if( item.getNodeType() != Node.ELEMENT_NODE )
259 continue;
260
261 int j = 0;
262 while( ( j < parts.size() ) && ( !item.getNodeName().equals( parts.get( j ).getName() ) ) )
263 {
264 j++;
265 }
266
267 if( j == parts.size() )
268 break;
269
270 parts.remove( j );
271 }
272 }
273
274
275 if( i == contentChildNodes.getLength() && parts.isEmpty() )
276 {
277 return wsdlOperation;
278 }
279 }
280 }
281 }
282
283 throw new DispatchException( "Missing operation for soapAction [" + soapAction + "] and body element ["
284 + contentQName + "] with SOAP Version [" + soapVersion + "]" );
285 }
286
287 @SuppressWarnings("unchecked")
288 public static WsdlOperation findOperationForResponse( SoapVersion soapVersion, String soapAction, XmlObject responseContent,
289 List<WsdlOperation> operations, boolean requireSoapVersionMatch, boolean requireSoapActionMatch ) throws Exception
290 {
291 XmlObject contentElm = getContentElement( responseContent, soapVersion );
292 if( contentElm == null )
293 throw new DispatchException( "Missing content element in body" );
294
295 QName contentQName = XmlUtils.getQName( contentElm.getDomNode() );
296 NodeList contentChildNodes = null;
297
298 for( int c = 0; c < operations.size(); c++ )
299 {
300 WsdlOperation wsdlOperation = operations.get( c );
301 String action = wsdlOperation.getAction();
302
303
304 if( !requireSoapActionMatch || (
305 ( soapAction == null && wsdlOperation.getAction() == null ) ||
306 ( action != null && action.equals( soapAction ))
307 ))
308 {
309 QName qname = wsdlOperation.getResponseBodyElementQName();
310
311 if( !contentQName.equals( qname ) )
312 continue;
313
314 SoapVersion ifaceSoapVersion = wsdlOperation.getInterface().getSoapVersion();
315
316 if( requireSoapVersionMatch && ifaceSoapVersion != soapVersion )
317 {
318 continue;
319 }
320
321
322 if( wsdlOperation.getStyle().equals( WsdlOperation.STYLE_DOCUMENT ) )
323 {
324
325 return wsdlOperation;
326 }
327 else if( wsdlOperation.getStyle().equals( WsdlOperation.STYLE_RPC ) )
328 {
329 BindingOperation bindingOperation = wsdlOperation.getBindingOperation();
330 Message message = bindingOperation.getOperation().getOutput().getMessage();
331 List<Part> parts = message.getOrderedParts( null );
332
333 if( contentChildNodes == null )
334 contentChildNodes = XmlUtils.getChildElements( ( Element ) contentElm.getDomNode() );
335
336 int i = 0;
337
338 if( parts.size() > 0 )
339 {
340 for( int x = 0; x < parts.size(); x++ )
341 {
342 if( WsdlUtils.isAttachmentOutputPart( parts.get( x ), bindingOperation ) ||
343 WsdlUtils.isHeaderOutputPart( parts.get( x ), message, bindingOperation ))
344 {
345 parts.remove( x );
346 x--;
347 }
348 }
349
350 for( ; i < contentChildNodes.getLength() && !parts.isEmpty(); i++ )
351 {
352 Node item = contentChildNodes.item( i );
353 if( item.getNodeType() != Node.ELEMENT_NODE )
354 continue;
355
356 int j = 0;
357 while( ( j < parts.size() ) && ( !item.getNodeName().equals( parts.get( j ).getName() ) ) )
358 {
359 j++;
360 }
361
362 if( j == parts.size() )
363 break;
364
365 parts.remove( j );
366 }
367 }
368
369
370 if( i == contentChildNodes.getLength() && parts.isEmpty() )
371 {
372 return wsdlOperation;
373 }
374 }
375 }
376 }
377
378 throw new DispatchException( "Missing response operation for soapAction [" + soapAction + "] and body element ["
379 + contentQName + "] with SOAP Version [" + soapVersion + "]" );
380 }
381
382
383 public static String removeEmptySoapHeaders( String content, SoapVersion soapVersion ) throws XmlException
384 {
385 XmlObject xmlObject = XmlObject.Factory.parse( content );
386 XmlObject[] selectPath = xmlObject.selectPath( "declare namespace soap='" + soapVersion.getEnvelopeNamespace() + "';/soap:Envelope/soap:Header" );
387 if( selectPath.length > 0 )
388 {
389 Node domNode = selectPath[0].getDomNode();
390 if( !domNode.hasChildNodes() && !domNode.hasAttributes())
391 {
392 domNode.getParentNode().removeChild( domNode );
393 return xmlObject.xmlText();
394 }
395 }
396
397 return content;
398 }
399
400 public static SoapVersion deduceSoapVersion(String requestContentType, String requestContent)
401 {
402 try
403 {
404 return deduceSoapVersion(requestContentType, XmlObject.Factory.parse(requestContent));
405 }
406 catch (XmlException e)
407 {
408 return deduceSoapVersion(requestContentType,(XmlObject)null);
409 }
410 }
411 }