1
2
3
4
5
6
7
8
9
10
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.model.iface.Attachment;
35 import com.eviware.soapui.support.StringUtils;
36 import com.eviware.soapui.support.types.StringToStringMap;
37 import com.eviware.soapui.support.xml.XmlUtils;
38
39 /***
40 * SOAP-related utility-methods..
41 *
42 * @author ole.matzura
43 */
44
45 public class SoapUtils
46 {
47 public static boolean isSoapFault( String responseContent, SoapVersion soapVersion ) throws XmlException
48 {
49 if( StringUtils.isNullOrEmpty( responseContent ) )
50 return false;
51
52
53 if( responseContent.indexOf( ":Fault" ) > 0 || responseContent.indexOf( "<Fault" ) > 0 )
54 {
55 XmlObject xml = XmlObject.Factory.parse( responseContent );
56 XmlObject[] paths = xml.selectPath( "declare namespace env='" + soapVersion.getEnvelopeNamespace() + "';"
57 + "//env:Fault" );
58 if( paths.length > 0 )
59 return true;
60 }
61
62 return false;
63 }
64
65 /***
66 * Init soapversion from content-type header.. should envelope be checked
67 * and/or override?
68 *
69 * @param xmlObject
70 */
71
72 public static SoapVersion deduceSoapVersion( String contentType, XmlObject xmlObject )
73 {
74 if( xmlObject != null )
75 {
76 Element elm = ( ( Document )( xmlObject.getDomNode() ) ).getDocumentElement();
77 if( elm.getLocalName().equals( "Envelope" ) )
78 {
79 if( elm.getNamespaceURI().equals( SoapVersion.Soap11.getEnvelopeNamespace() ) )
80 return SoapVersion.Soap11;
81 else if( elm.getNamespaceURI().equals( SoapVersion.Soap12.getEnvelopeNamespace() ) )
82 return SoapVersion.Soap12;
83 }
84 }
85
86 SoapVersion soapVersion = null;
87
88 if( StringUtils.isNullOrEmpty( contentType ) )
89 return null;
90
91 soapVersion = contentType.startsWith( SoapVersion.Soap11.getContentType() ) ? SoapVersion.Soap11 : null;
92 soapVersion = soapVersion == null && contentType.startsWith( SoapVersion.Soap12.getContentType() ) ? SoapVersion.Soap12
93 : soapVersion;
94 if( soapVersion == null && contentType.startsWith( "application/xop+xml" ) )
95 {
96 if( contentType.indexOf( "type=\"" + SoapVersion.Soap11.getContentType() + "\"" ) > 0 )
97 soapVersion = SoapVersion.Soap11;
98 else if( contentType.indexOf( "type=\"" + SoapVersion.Soap12.getContentType() + "\"" ) > 0 )
99 soapVersion = SoapVersion.Soap12;
100 }
101
102 return soapVersion;
103 }
104
105 public static String getSoapAction( SoapVersion soapVersion, StringToStringMap headers )
106 {
107 String soapAction = null;
108 String contentType = headers.get( "Content-Type" );
109
110 if( soapVersion == SoapVersion.Soap11 )
111 {
112 soapAction = headers.get( "SOAPAction" );
113 }
114 else if( soapVersion == SoapVersion.Soap12 )
115 {
116 int ix = contentType.indexOf( "action=" );
117 if( ix > 0 )
118 {
119 int endIx = contentType.indexOf( ';', ix );
120 soapAction = endIx == -1 ? contentType.substring( ix + 7 ) : contentType.substring( ix + 7, endIx );
121 }
122 }
123
124 soapAction = StringUtils.unquote( soapAction );
125
126 return soapAction;
127 }
128
129 public static XmlObject getBodyElement( XmlObject messageObject, SoapVersion soapVersion ) throws XmlException
130 {
131 XmlObject[] envelope = messageObject.selectChildren( soapVersion.getEnvelopeQName() );
132 if( envelope.length != 1 )
133 throw new XmlException( "Missing/Invalid SOAP Envelope, expecting [" + soapVersion.getEnvelopeQName() + "]" );
134
135 XmlObject[] body = envelope[0].selectChildren( soapVersion.getBodyQName() );
136 if( body.length != 1 )
137 throw new XmlException( "Missing/Invalid SOAP Body, expecting [" + soapVersion.getBodyQName() + "]" );
138
139 return body[0];
140 }
141
142 public static XmlObject getHeaderElement( XmlObject messageObject, SoapVersion soapVersion, boolean create )
143 throws XmlException
144 {
145 XmlObject[] envelope = messageObject.selectChildren( soapVersion.getEnvelopeQName() );
146 if( envelope.length != 1 )
147 throw new XmlException( "Missing/Invalid SOAP Envelope, expecting [" + soapVersion.getEnvelopeQName() + "]" );
148
149 QName headerQName = soapVersion.getHeaderQName();
150 XmlObject[] header = envelope[0].selectChildren( headerQName );
151 if( header.length == 0 && create )
152 {
153 Element elm = ( Element )envelope[0].getDomNode();
154 Element headerElement = elm.getOwnerDocument().createElementNS( headerQName.getNamespaceURI(),
155 headerQName.getLocalPart() );
156
157 elm.insertBefore( headerElement, elm.getFirstChild() );
158
159 header = envelope[0].selectChildren( headerQName );
160 }
161
162 return header.length == 0 ? null : header[0];
163 }
164
165 public static XmlObject getContentElement( XmlObject messageObject, SoapVersion soapVersion ) throws XmlException
166 {
167 XmlObject bodyElement = SoapUtils.getBodyElement( messageObject, soapVersion );
168 if( bodyElement != null )
169 {
170 XmlCursor cursor = bodyElement.newCursor();
171
172 try
173 {
174 if( cursor.toFirstChild() )
175 {
176 while( !cursor.isContainer() )
177 cursor.toNextSibling();
178
179 if( cursor.isContainer() )
180 {
181 return cursor.getObject();
182 }
183 }
184 }
185 catch( Exception e )
186 {
187 SoapUI.logError( e );
188 }
189 finally
190 {
191 cursor.dispose();
192 }
193 }
194
195 return null;
196 }
197
198 @SuppressWarnings( "unchecked" )
199 public static WsdlOperation findOperationForRequest( SoapVersion soapVersion, String soapAction,
200 XmlObject requestContent, List<WsdlOperation> operations, boolean requireSoapVersionMatch,
201 boolean requireSoapActionMatch, Attachment[] attachments ) throws Exception
202 {
203 XmlObject contentElm = getContentElement( requestContent, soapVersion );
204 if( contentElm == null )
205 {
206 for( WsdlOperation operation : operations )
207 {
208 if( operation.getAction().equals( soapAction )
209 && operation.getBindingOperation().getOperation().getInput().getMessage().getParts().size() == 0 )
210 {
211 return operation;
212 }
213 }
214 }
215
216 QName contentQName = XmlUtils.getQName( contentElm.getDomNode() );
217 NodeList contentChildNodes = null;
218
219 for( int c = 0; c < operations.size(); c++ )
220 {
221 WsdlOperation wsdlOperation = operations.get( c );
222 String action = wsdlOperation.getAction();
223
224
225 if( !requireSoapActionMatch
226 || ( ( soapAction == null && wsdlOperation.getAction() == null ) || ( action != null && action
227 .equals( soapAction ) ) ) )
228 {
229 QName qname = wsdlOperation.getRequestBodyElementQName();
230
231 if( !contentQName.equals( qname ) )
232 continue;
233
234 SoapVersion ifaceSoapVersion = wsdlOperation.getInterface().getSoapVersion();
235
236 if( requireSoapVersionMatch && ifaceSoapVersion != soapVersion )
237 {
238 continue;
239 }
240
241
242 if( wsdlOperation.getStyle().equals( WsdlOperation.STYLE_DOCUMENT ) )
243 {
244
245 BindingOperation bindingOperation = wsdlOperation.getBindingOperation();
246 Message message = bindingOperation.getOperation().getInput().getMessage();
247 List<Part> parts = message.getOrderedParts( null );
248
249 for( int x = 0; x < parts.size(); x++ )
250 {
251
252 if( WsdlUtils.isAttachmentInputPart( parts.get( x ), bindingOperation ) )
253 {
254 for( Attachment attachment : attachments )
255 {
256 if( attachment.getPart().equals( parts.get( x ).getName() ) )
257 {
258 parts.remove( x );
259 x-- ;
260 }
261 }
262 }
263 else
264 {
265 parts.remove( x );
266 x-- ;
267 }
268 }
269
270
271 if( parts.isEmpty() )
272 {
273 return wsdlOperation;
274 }
275 }
276 else if( wsdlOperation.getStyle().equals( WsdlOperation.STYLE_RPC ) )
277 {
278 BindingOperation bindingOperation = wsdlOperation.getBindingOperation();
279 Message message = bindingOperation.getOperation().getInput().getMessage();
280 List<Part> parts = message.getOrderedParts( null );
281
282 if( contentChildNodes == null )
283 contentChildNodes = XmlUtils.getChildElements( ( Element )contentElm.getDomNode() );
284
285 int i = 0;
286
287 if( parts.size() > 0 )
288 {
289 for( int x = 0; x < parts.size(); x++ )
290 {
291 if( WsdlUtils.isAttachmentInputPart( parts.get( x ), bindingOperation ) )
292 {
293 for( Attachment attachment : attachments )
294 {
295 if( attachment.getPart().equals( parts.get( x ).getName() ) )
296 {
297 parts.remove( x );
298 x-- ;
299 }
300 }
301 }
302
303
304 if( x >= 0 && WsdlUtils.isHeaderInputPart( parts.get( x ), message, bindingOperation ) )
305 {
306 parts.remove( x );
307 x-- ;
308 }
309 }
310
311 for( ; i < contentChildNodes.getLength() && !parts.isEmpty(); i++ )
312 {
313 Node item = contentChildNodes.item( i );
314 if( item.getNodeType() != Node.ELEMENT_NODE )
315 continue;
316
317 int j = 0;
318 while( ( j < parts.size() ) && ( !item.getNodeName().equals( parts.get( j ).getName() ) ) )
319 {
320 Part part = parts.get( j );
321 if( part.getElementName() != null )
322 {
323 QName qn = part.getElementName();
324 if( item.getLocalName().equals( qn.getLocalPart() )
325 && item.getNamespaceURI().equals( qn.getNamespaceURI() ) )
326 break;
327 }
328 else
329 {
330 if( item.getNodeName().equals( parts.get( j ).getName() ) )
331 break;
332 }
333
334 j++ ;
335 }
336
337 if( j == parts.size() )
338 break;
339
340 parts.remove( j );
341 }
342 }
343
344
345 if( i == contentChildNodes.getLength() && parts.isEmpty() )
346 {
347 return wsdlOperation;
348 }
349 }
350 }
351 }
352
353 throw new DispatchException( "Missing operation for soapAction [" + soapAction + "] and body element ["
354 + contentQName + "] with SOAP Version [" + soapVersion + "]" );
355 }
356
357 @SuppressWarnings( "unchecked" )
358 public static WsdlOperation findOperationForResponse( SoapVersion soapVersion, String soapAction,
359 XmlObject responseContent, List<WsdlOperation> operations, boolean requireSoapVersionMatch,
360 boolean requireSoapActionMatch ) throws Exception
361 {
362 XmlObject contentElm = getContentElement( responseContent, soapVersion );
363 if( contentElm == null )
364 return null;
365
366 QName contentQName = XmlUtils.getQName( contentElm.getDomNode() );
367 NodeList contentChildNodes = null;
368
369 for( int c = 0; c < operations.size(); c++ )
370 {
371 WsdlOperation wsdlOperation = operations.get( c );
372 String action = wsdlOperation.getAction();
373
374
375 if( !requireSoapActionMatch
376 || ( ( soapAction == null && wsdlOperation.getAction() == null ) || ( action != null && action
377 .equals( soapAction ) ) ) )
378 {
379 QName qname = wsdlOperation.getResponseBodyElementQName();
380
381 if( !contentQName.equals( qname ) )
382 continue;
383
384 SoapVersion ifaceSoapVersion = wsdlOperation.getInterface().getSoapVersion();
385
386 if( requireSoapVersionMatch && ifaceSoapVersion != soapVersion )
387 {
388 continue;
389 }
390
391
392 if( wsdlOperation.getStyle().equals( WsdlOperation.STYLE_DOCUMENT ) )
393 {
394
395 return wsdlOperation;
396 }
397 else if( wsdlOperation.getStyle().equals( WsdlOperation.STYLE_RPC ) )
398 {
399 BindingOperation bindingOperation = wsdlOperation.getBindingOperation();
400 Message message = bindingOperation.getOperation().getOutput().getMessage();
401 List<Part> parts = message.getOrderedParts( null );
402
403 if( contentChildNodes == null )
404 contentChildNodes = XmlUtils.getChildElements( ( Element )contentElm.getDomNode() );
405
406 int i = 0;
407
408 if( parts.size() > 0 )
409 {
410 for( int x = 0; x < parts.size(); x++ )
411 {
412 if( WsdlUtils.isAttachmentOutputPart( parts.get( x ), bindingOperation )
413 || WsdlUtils.isHeaderOutputPart( parts.get( x ), message, bindingOperation ) )
414 {
415 parts.remove( x );
416 x-- ;
417 }
418 }
419
420 for( ; i < contentChildNodes.getLength() && !parts.isEmpty(); i++ )
421 {
422 Node item = contentChildNodes.item( i );
423 if( item.getNodeType() != Node.ELEMENT_NODE )
424 continue;
425
426 int j = 0;
427 while( ( j < parts.size() ) && ( !item.getNodeName().equals( parts.get( j ).getName() ) ) )
428 {
429 Part part = parts.get( j );
430 if( part.getElementName() != null )
431 {
432 QName qn = part.getElementName();
433 if( item.getLocalName().equals( qn.getLocalPart() )
434 && item.getNamespaceURI().equals( qn.getNamespaceURI() ) )
435 break;
436 }
437 else
438 {
439 if( item.getNodeName().equals( parts.get( j ).getName() ) )
440 break;
441 }
442
443 j++ ;
444 }
445
446 if( j == parts.size() )
447 break;
448
449 parts.remove( j );
450 }
451 }
452
453
454 if( i == contentChildNodes.getLength() && parts.isEmpty() )
455 {
456 return wsdlOperation;
457 }
458 }
459 }
460 }
461
462 throw new DispatchException( "Missing response operation for soapAction [" + soapAction + "] and body element ["
463 + contentQName + "] with SOAP Version [" + soapVersion + "]" );
464 }
465
466 public static String removeEmptySoapHeaders( String content, SoapVersion soapVersion ) throws XmlException
467 {
468 XmlObject xmlObject = XmlObject.Factory.parse( content );
469 XmlObject[] selectPath = xmlObject.selectPath( "declare namespace soap='" + soapVersion.getEnvelopeNamespace()
470 + "';/soap:Envelope/soap:Header" );
471 if( selectPath.length > 0 )
472 {
473 Node domNode = selectPath[0].getDomNode();
474 if( !domNode.hasChildNodes() && !domNode.hasAttributes() )
475 {
476 domNode.getParentNode().removeChild( domNode );
477 return xmlObject.xmlText();
478 }
479 }
480
481 return content;
482 }
483
484 public static SoapVersion deduceSoapVersion( String requestContentType, String requestContent )
485 {
486 try
487 {
488 return deduceSoapVersion( requestContentType, XmlObject.Factory.parse( requestContent ) );
489 }
490 catch( XmlException e )
491 {
492 return deduceSoapVersion( requestContentType, ( XmlObject )null );
493 }
494 }
495
496 public static String transferSoapHeaders( String requestContent, String newRequest, SoapVersion soapVersion )
497 {
498 try
499 {
500 XmlObject source = XmlObject.Factory.parse( requestContent );
501 String headerXPath = "declare namespace ns='" + soapVersion.getEnvelopeNamespace() + "'; //ns:Header";
502 XmlObject[] header = source.selectPath( headerXPath );
503 if( header.length == 1 )
504 {
505 Element headerElm = ( Element )header[0].getDomNode();
506 NodeList childNodes = headerElm.getChildNodes();
507 if( childNodes.getLength() > 0 )
508 {
509 XmlObject dest = XmlObject.Factory.parse( newRequest );
510 header = dest.selectPath( headerXPath );
511 Element destElm = null;
512
513 if( header.length == 0 )
514 {
515 Element docElm = ( ( Document )dest.getDomNode() ).getDocumentElement();
516
517 destElm = ( Element )docElm.insertBefore( docElm.getOwnerDocument().createElementNS(
518 soapVersion.getEnvelopeNamespace(), docElm.getPrefix() + ":Header" ), XmlUtils
519 .getFirstChildElementNS( docElm, soapVersion.getBodyQName() ) );
520 }
521 else
522 {
523 destElm = ( Element )header[0].getDomNode();
524 }
525
526 for( int c = 0; c < childNodes.getLength(); c++ )
527 {
528 destElm.appendChild( destElm.getOwnerDocument().importNode( childNodes.item( c ), true ) );
529 }
530
531 return dest.xmlText();
532 }
533 }
534 }
535 catch( XmlException e )
536 {
537 SoapUI.logError( e );
538 }
539
540 return newRequest;
541 }
542 }