1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.impl.wsdl.support.wsdl;
14
15 import com.eviware.soapui.SoapUI;
16 import com.eviware.soapui.impl.wsdl.WsdlOperation;
17 import com.eviware.soapui.impl.wsdl.submit.WsdlMessageExchange;
18 import com.eviware.soapui.model.iface.Attachment;
19 import com.eviware.soapui.model.testsuite.AssertionError;
20 import com.eviware.soapui.settings.WsdlSettings;
21 import com.eviware.soapui.support.xml.XmlUtils;
22 import org.apache.log4j.Logger;
23 import org.apache.xmlbeans.*;
24 import org.w3c.dom.Element;
25
26 import javax.wsdl.*;
27 import javax.wsdl.extensions.mime.MIMEContent;
28 import javax.xml.namespace.QName;
29 import java.util.ArrayList;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33
34 /***
35 * Class for validating SOAP requests/responses against their definition and schema, requires that
36 * the messages follow basic-profile requirements
37 *
38 * @author Ole.Matzura
39 */
40
41 public class WsdlValidator
42 {
43 private final WsdlContext wsdlContext;
44 private final static Logger log = Logger.getLogger( WsdlValidator.class );
45
46 public WsdlValidator( WsdlContext wsdlContext )
47 {
48 this.wsdlContext = wsdlContext;
49 }
50
51 public AssertionError [] assertRequest( WsdlMessageExchange messageExchange, boolean envelopeOnly )
52 {
53 List<XmlError> errors = new ArrayList<XmlError>();
54 try
55 {
56 String requestContent = messageExchange.getRequestContent();
57 wsdlContext.getSoapVersion().validateSoapEnvelope(requestContent, errors);
58
59 if (errors.isEmpty() && !envelopeOnly )
60 {
61 wsdlContext.getSoapVersion().validateSoapEnvelope(requestContent, errors);
62 WsdlOperation operation = messageExchange.getOperation();
63 BindingOperation bindingOperation = operation.getBindingOperation();
64 if (bindingOperation == null)
65 {
66 errors.add(XmlError.forMessage("Missing operation ["
67 + operation.getBindingOperationName() + "] in wsdl definition"));
68 }
69 else
70 {
71 Part[] inputParts = WsdlUtils.getInputParts(bindingOperation);
72 validateMessage(messageExchange, requestContent, bindingOperation, inputParts, errors, false);
73
74 }
75 }
76 }
77 catch (Exception e)
78 {
79 errors.add( XmlError.forMessage( e.getMessage() ));
80 }
81
82 return convertErrors( errors );
83 }
84
85 private void validateInputAttachments(WsdlMessageExchange messageExchange, List<XmlError> errors, BindingOperation bindingOperation, Part[] inputParts)
86 {
87 for( Part part : inputParts )
88 {
89 MIMEContent[] contents = WsdlUtils.getInputMultipartContent( part, bindingOperation );
90 if( contents.length == 0 )
91 continue;
92
93 Attachment [] attachments = messageExchange.getRequestAttachmentsForPart( part.getName() );
94 if( attachments.length == 0 )
95 {
96 errors.add(XmlError.forMessage("Missing attachment for part [" + part.getName() + "]" ));
97 }
98 else if( attachments.length == 1 )
99 {
100 Attachment attachment = attachments[0];
101 String types = "";
102 for( MIMEContent content : contents )
103 {
104 String type = content.getType();
105 if( type.equals( attachment.getContentType() ) || type.toUpperCase().startsWith( "MULTIPART" ))
106 {
107 types = null;
108 break;
109 }
110 if( types.length() > 0 )
111 types += ",";
112
113 types += type;
114 }
115
116 if( types != null )
117 {
118 String msg = "Missing attachment for part [" + part.getName() +"] with content-type [" + types + "]," +
119 " content type is [" + attachment.getContentType() + "]";
120
121 if( SoapUI.getSettings().getBoolean( WsdlSettings.ALLOW_INCORRECT_CONTENTTYPE ))
122 log.warn( msg );
123 else
124 errors.add(XmlError.forMessage(msg ));
125 }
126 }
127 else
128 {
129 String types = "";
130 for( MIMEContent content : contents )
131 {
132 String type = content.getType();
133 if( type.toUpperCase().startsWith( "MULTIPART" ))
134 {
135 types = null;
136 break;
137 }
138 if( types.length() > 0 )
139 types += ",";
140
141 types += type;
142 }
143
144 if( types == null )
145 {
146 String msg = "Too many attachments for part [" + part.getName() + "] with content-type [" + types + "]";
147 if( SoapUI.getSettings().getBoolean( WsdlSettings.ALLOW_INCORRECT_CONTENTTYPE ))
148 log.warn( msg );
149 else
150 errors.add(XmlError.forMessage(msg ));
151 }
152 }
153
154 if( attachments.length > 0 )
155 validateAttachmentsReadability( errors, attachments );
156 }
157 }
158
159 private void validateOutputAttachments(WsdlMessageExchange messageExchange, XmlObject xml, List<XmlError> errors, BindingOperation bindingOperation, Part[] outputParts) throws Exception
160 {
161 for( Part part : outputParts )
162 {
163 MIMEContent[] contents = WsdlUtils.getOutputMultipartContent( part, bindingOperation );
164 if( contents.length == 0 )
165 continue;
166
167 Attachment [] attachments = messageExchange.getResponseAttachmentsForPart( part.getName() );
168
169
170 if( attachments.length == 0 && WsdlUtils.isRpc( wsdlContext.getDefinition(), bindingOperation ))
171 {
172 XmlObject[] rpcBodyPart = getRpcBodyPart( bindingOperation, xml, true );
173 if( rpcBodyPart.length == 1 )
174 {
175 XmlObject[] children = rpcBodyPart[0].selectChildren( new QName( part.getName() ));
176 if( children.length == 1 )
177 {
178 String href = ((Element)children[0].getDomNode()).getAttribute( "href" );
179 if( href != null )
180 {
181 if( href.startsWith( "cid:" ))
182 href = href.substring( 4 );
183
184 attachments = messageExchange.getResponseAttachmentsForPart( href );
185 }
186 }
187 }
188 }
189
190 if( attachments.length == 0 )
191 {
192 errors.add(XmlError.forMessage("Missing attachment for part [" + part.getName() + "]" ));
193 }
194 else if( attachments.length == 1 )
195 {
196 Attachment attachment = attachments[0];
197 String types = "";
198 for( MIMEContent content : contents )
199 {
200 String type = content.getType();
201 if( type.equals( attachment.getContentType() ) || type.toUpperCase().startsWith( "MULTIPART" ))
202 {
203 types = null;
204 break;
205 }
206
207 if( types.length() > 0 )
208 types += ",";
209
210 types += type;
211 }
212
213 if( types != null)
214 {
215 String msg = "Missing attachment for part [" + part.getName() +
216 "] with content-type [" + types + "], content type is [" + attachment.getContentType() + "]";
217
218 if( SoapUI.getSettings().getBoolean( WsdlSettings.ALLOW_INCORRECT_CONTENTTYPE ))
219 log.warn( msg );
220 else
221 errors.add(XmlError.forMessage(msg ));
222 }
223 }
224 else
225 {
226 String types = "";
227 for( MIMEContent content : contents )
228 {
229 String type = content.getType();
230 if( type.toUpperCase().startsWith( "MULTIPART" ))
231 {
232 types = null;
233 break;
234 }
235
236 if( types.length() > 0 )
237 types += ",";
238
239 types += type;
240 }
241
242 if( types != null )
243 {
244 String msg = "Too many attachments for part [" + part.getName() + "] with content-type [" + types + "]";
245
246 if( SoapUI.getSettings().getBoolean( WsdlSettings.ALLOW_INCORRECT_CONTENTTYPE ))
247 log.warn( msg );
248 else
249 errors.add(XmlError.forMessage(msg ));
250 }
251 }
252
253 if( attachments.length > 0 )
254 validateAttachmentsReadability( errors, attachments );
255 }
256 }
257
258 private void validateAttachmentsReadability( List<XmlError> errors, Attachment[] attachments )
259 {
260 for( Attachment attachment : attachments )
261 {
262 try
263 {
264 attachment.getInputStream();
265 }
266 catch( Exception e )
267 {
268 errors.add(XmlError.forMessage(e.toString() ));
269 }
270 }
271 }
272
273 public XmlObject [] getMessageParts( String messageContent, String operationName, boolean isResponse ) throws Exception
274 {
275 BindingOperation bindingOperation = findBindingOperation(operationName);
276 if (bindingOperation == null)
277 {
278 throw new Exception("Missing operation [" + operationName + "] in wsdl definition");
279 }
280
281 if( !wsdlContext.hasSchemaTypes() )
282 {
283 throw new Exception("Missing schema types for message");
284 }
285
286 XmlObject msgXml = XmlObject.Factory.parse( messageContent );
287 Part[] parts = isResponse ? WsdlUtils.getOutputParts( bindingOperation ) : WsdlUtils.getInputParts(bindingOperation);
288 if( parts == null || parts.length == 0 )
289 throw new Exception( "Missing parts for operation [" + operationName + "]" );
290
291 List<XmlObject> result = new ArrayList<XmlObject>();
292
293 if( WsdlUtils.isRpc( wsdlContext.getDefinition(), bindingOperation ))
294 {
295
296 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" +
297 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" +
298 "declare namespace ns='" + wsdlContext.getDefinition().getTargetNamespace() + "';" +
299 "$this/env:Envelope/env:Body/ns:" + bindingOperation.getName() + (isResponse ? "Response" : ""));
300
301 if( paths.length != 1 )
302 {
303 throw new Exception("Missing message wrapper element [" +
304 wsdlContext.getDefinition().getTargetNamespace() + "@" + bindingOperation.getName() + (isResponse ? "Response" : ""));
305 }
306 else
307 {
308 XmlObject wrapper = paths[0];
309
310 for (int i = 0; i < parts.length; i++)
311 {
312 Part part = parts[i];
313 if( (isResponse && WsdlUtils.isAttachmentOutputPart( part, bindingOperation )) ||
314 (!isResponse && WsdlUtils.isAttachmentInputPart( part, bindingOperation )))
315 continue;
316
317 QName partName = part.getElementName();
318 if( partName == null )
319 partName = new QName( part.getName() );
320
321 XmlObject[] children = wrapper.selectChildren( partName );
322 if( children.length != 1 )
323 {
324 log.error("Missing message part [" + part.getName() + "]" );
325 }
326 else
327 {
328 QName typeName = part.getTypeName();
329 if( typeName == null )
330 {
331 typeName = partName;
332 SchemaGlobalElement type = wsdlContext.getSchemaTypeLoader().findElement( typeName );
333
334 if( type != null )
335 {
336 result.add( children[0].copy().changeType( type.getType() ));
337 }
338 else log.error( "Missing element [" + typeName + "] in associated schema for part [" + part.getName() + "]" );
339 }
340 else
341 {
342 SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
343 if( type != null )
344 {
345 result.add( children[0].copy().changeType( type ));
346 }
347 else log.error( "Missing type [" + typeName + "] in associated schema for part [" + part.getName() + "]" );
348 }
349 }
350 }
351 }
352 }
353 else
354 {
355 Part part = parts[0];
356 QName elementName = part.getElementName();
357 if( elementName != null )
358 {
359
360 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" +
361 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" +
362 "declare namespace ns='" + elementName.getNamespaceURI() + "';" +
363 "$this/env:Envelope/env:Body/ns:" + elementName.getLocalPart() );
364
365 if( paths.length == 1 )
366 {
367 SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement( elementName );
368 if( elm != null )
369 {
370 result.add( paths[0].copy().changeType( elm.getType() ));
371 }
372 else throw new Exception("Missing part type in associated schema" );
373 }
374 else throw new Exception("Missing message part with name [" + elementName + "]" );
375 }
376 }
377
378 return result.toArray( new XmlObject[result.size()] );
379 }
380
381
382 @SuppressWarnings("unchecked")
383 public void validateXml(String request, List<XmlError> errors )
384 {
385 try
386 {
387 XmlOptions xmlOptions = new XmlOptions();
388 xmlOptions.setLoadLineNumbers();
389 xmlOptions.setErrorListener(errors);
390 xmlOptions.setLoadLineNumbers(XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT);
391 XmlObject.Factory.parse(request, xmlOptions);
392 }
393 catch( XmlException e )
394 {
395 if( e.getErrors() != null )
396 errors.addAll( e.getErrors() );
397 errors.add( XmlError.forMessage( e.getMessage() ));
398 }
399 catch (Exception e)
400 {
401 errors.add( XmlError.forMessage( e.getMessage() ));
402 }
403 }
404
405 private AssertionError[] convertErrors(List<XmlError> errors)
406 {
407 if( errors.size() > 0 )
408 {
409 List<AssertionError> response = new ArrayList<AssertionError>();
410 for (Iterator<XmlError> i = errors.iterator(); i.hasNext();)
411 {
412 XmlError error = i.next();
413
414 if( error instanceof XmlValidationError )
415 {
416 XmlValidationError e = ((XmlValidationError)error);
417 QName offendingQName = e.getOffendingQName();
418 if( offendingQName != null )
419 {
420 if( offendingQName.equals( new QName( wsdlContext.getSoapVersion().getEnvelopeNamespace(), "encodingStyle")))
421 {
422 log.debug( "ignoring encodingStyle validation..");
423 continue;
424 }
425 else if( offendingQName.equals( new QName( wsdlContext.getSoapVersion().getEnvelopeNamespace(), "mustUnderstand")))
426 {
427 log.debug( "ignoring mustUnderstand validation..");
428 continue;
429 }
430 }
431 }
432
433 AssertionError assertionError = new AssertionError(error);
434 if( !response.contains( assertionError ))
435 response.add( assertionError );
436 }
437
438 return response.toArray( new AssertionError[response.size()] );
439 }
440
441 return new AssertionError[0];
442 }
443
444 @SuppressWarnings("unchecked")
445 public void validateMessage( WsdlMessageExchange messageExchange, String message, BindingOperation bindingOperation, Part [] parts, List<XmlError> errors, boolean isResponse )
446 {
447 try
448 {
449 if( !wsdlContext.hasSchemaTypes() )
450 {
451 errors.add( XmlError.forMessage( "Missing schema types for message"));
452 }
453 else
454 {
455 if( !WsdlUtils.isOutputSoapEncoded( bindingOperation))
456 {
457 XmlOptions xmlOptions = new XmlOptions();
458 xmlOptions.setLoadLineNumbers();
459 xmlOptions.setLoadLineNumbers(XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT);
460 XmlObject xml = XmlObject.Factory.parse( message, xmlOptions );
461
462 XmlObject[] paths = xml.selectPath( "declare namespace env='" +
463 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" +
464 "$this/env:Envelope/env:Body/env:Fault");
465
466 if( paths.length > 0 )
467 {
468 validateSoapFault( bindingOperation, paths[0], errors );
469 }
470 else if( WsdlUtils.isRpc( wsdlContext.getDefinition(), bindingOperation ))
471 {
472 validateRpcLiteral( bindingOperation, parts, xml, errors, isResponse );
473 }
474 else
475 {
476 validateDocLiteral( bindingOperation, parts, xml, errors, isResponse );
477 }
478
479 if( isResponse )
480 validateOutputAttachments( messageExchange, xml, errors, bindingOperation, parts );
481 else
482 validateInputAttachments( messageExchange, errors, bindingOperation, parts );
483 }
484 else errors.add( XmlError.forMessage( "Validation of SOAP-Encoded messages not supported"));
485 }
486 }
487 catch ( XmlException e )
488 {
489 if( e.getErrors() != null )
490 errors.addAll( e.getErrors() );
491 errors.add( XmlError.forMessage( e.getMessage() ));
492 }
493 catch (Exception e)
494 {
495 errors.add( XmlError.forMessage( e.getMessage() ));
496 }
497 }
498
499 private BindingOperation findBindingOperation(String operationName) throws Exception
500 {
501 Map<?,?> services = wsdlContext.getDefinition().getAllServices();
502 Iterator<?> i = services.keySet().iterator();
503 while( i.hasNext() )
504 {
505 Service service = (Service) wsdlContext.getDefinition().getService( (QName) i.next());
506 Map<?,?> ports = service.getPorts();
507
508 Iterator<?> iterator = ports.keySet().iterator();
509 while( iterator.hasNext() )
510 {
511 Port port = (Port) service.getPort( (String) iterator.next() );
512 Binding binding = port.getBinding();
513 if( binding.getQName().equals( wsdlContext.getInterface().getBindingName() ))
514 {
515 BindingOperation bindingOperation = binding.getBindingOperation( operationName, null, null );
516 if( bindingOperation != null ) return bindingOperation;
517 }
518 }
519 }
520
521 Map<?,?> bindings = wsdlContext.getDefinition().getAllBindings();
522 i = bindings.keySet().iterator();
523 while( i.hasNext() )
524 {
525 Binding binding = (Binding) bindings.get( i.next() );
526 if( binding.getQName().equals( wsdlContext.getInterface().getBindingName() ))
527 {
528 BindingOperation bindingOperation = binding.getBindingOperation( operationName, null, null );
529 if( bindingOperation != null ) return bindingOperation;
530 }
531 }
532
533 return null;
534 }
535
536 public AssertionError [] assertResponse( WsdlMessageExchange messageExchange, boolean envelopeOnly )
537 {
538 List<XmlError> errors = new ArrayList<XmlError>();
539 try
540 {
541 String response = messageExchange.getResponseContent();
542 wsdlContext.getSoapVersion().validateSoapEnvelope(response, errors);
543
544 if (errors.isEmpty() && !envelopeOnly )
545 {
546 WsdlOperation operation = messageExchange.getOperation();
547 BindingOperation bindingOperation = operation.getBindingOperation();
548 if (bindingOperation == null)
549 {
550 errors.add(XmlError.forMessage("Missing operation ["
551 + operation.getBindingOperationName() + "] in wsdl definition"));
552 }
553 else
554 {
555 Part[] outputParts = WsdlUtils.getOutputParts(bindingOperation);
556 validateMessage(messageExchange, response, bindingOperation, outputParts, errors, true);
557 }
558 }
559 }
560 catch (Exception e)
561 {
562 e.printStackTrace( );
563 errors.add( XmlError.forMessage( e.getMessage() ));
564 }
565
566 return convertErrors( errors );
567 }
568
569 private void validateDocLiteral(BindingOperation bindingOperation, Part[] parts, XmlObject msgXml, List<XmlError> errors, boolean isResponse) throws Exception
570 {
571 Part part = null;
572
573
574 for( int c = 0; c < parts.length; c++ )
575 {
576
577 if( (isResponse && !WsdlUtils.isAttachmentOutputPart( parts[c], bindingOperation )) ||
578 (!isResponse && !WsdlUtils.isAttachmentInputPart( parts[c], bindingOperation )))
579 {
580
581 if( part != null )
582 {
583 errors.add( XmlError.forMessage("DocLiteral message must contain 1 body part definition" ));
584 return;
585 }
586
587 part = parts[c];
588 }
589 }
590
591 QName elementName = part.getElementName();
592 if( elementName != null )
593 {
594
595 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" +
596 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" +
597 "declare namespace ns='" + elementName.getNamespaceURI() + "';" +
598 "$this/env:Envelope/env:Body/ns:" + elementName.getLocalPart() );
599
600 if( paths.length == 1 )
601 {
602 SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement( elementName );
603 if( elm != null )
604 {
605 validateMessageBody(errors, elm.getType(), paths[0]);
606 }
607 else errors.add( XmlError.forMessage("Missing part type [" + elementName + "] in associated schema") );
608 }
609 else errors.add( XmlError.forMessage("Missing message part with name [" + elementName + "]" ));
610 }
611 else if( part.getTypeName() != null )
612 {
613 QName typeName = part.getTypeName();
614
615 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" +
616 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" +
617 "declare namespace ns='" + typeName.getNamespaceURI() + "';" +
618 "$this/env:Envelope/env:Body/ns:" + part.getName() );
619
620 if( paths.length == 1 )
621 {
622 SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
623 if( type != null )
624 {
625 validateMessageBody( errors, type, paths[0] );
626
627
628 }
629 else errors.add(XmlError.forMessage( "Missing part type in associated schema") );
630 }
631 else errors.add( XmlError.forMessage("Missing message part with name:type [" +
632 part.getName() + ":" + typeName + "]" ));
633 }
634 }
635
636 private void validateMessageBody(List<XmlError> errors, SchemaType type, XmlObject msg) throws XmlException
637 {
638
639
640 XmlOptions xmlOptions = new XmlOptions();
641 xmlOptions.setLoadLineNumbers();
642 xmlOptions.setLoadLineNumbers(XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT);
643
644 String xmlText = msg.copy().changeType( type ).xmlText( xmlOptions.setSaveOuter());
645 XmlObject obj = type.getTypeSystem().parse( xmlText, type, xmlOptions );
646 obj = obj.changeType( type );
647
648
649 List<?> list = new ArrayList<Object>();
650
651 xmlOptions = new XmlOptions();
652 xmlOptions.setErrorListener( list );
653 xmlOptions.setValidateTreatLaxAsSkip();
654 obj.validate( xmlOptions );
655
656
657 for( int c = 0; c < list.size(); c++ )
658 {
659 XmlError error = (XmlError) list.get( c );
660
661 if( error instanceof XmlValidationError )
662 {
663 XmlValidationError validationError = ((XmlValidationError)error);
664
665 if( wsdlContext.getSoapVersion().shouldIgnore( validationError ))
666 continue;
667
668
669 if( validationError.getErrorCode().equals( "base64Binary") || validationError.getErrorCode().equals( "hexBinary"))
670 {
671 XmlCursor cursor = validationError.getCursorLocation();
672 if( cursor.toParent() )
673 {
674 String text = cursor.getTextValue();
675
676
677 if( text.startsWith( "cid:" ) || text.startsWith( "file:" ))
678 {
679
680 continue;
681 }
682 }
683 }
684 }
685
686 int line = error.getLine() == -1 ? 0 : error.getLine()-1;
687 errors.add( XmlError.forLocation( error.getMessage(), error.getSourceName(),
688 getLine( msg ) + line, error.getColumn(), error.getOffset() ));
689 }
690 }
691
692 private int getLine(XmlObject object)
693 {
694 List<?> list = new ArrayList<Object>();
695 object.newCursor().getAllBookmarkRefs( list );
696 for( int c = 0; c < list.size(); c++ )
697 {
698 if( list.get( c ) instanceof XmlLineNumber )
699 {
700 return ((XmlLineNumber)list.get(c)).getLine();
701 }
702 }
703
704 return -1;
705 }
706
707 private void validateRpcLiteral(BindingOperation bindingOperation, Part[] parts, XmlObject msgXml, List<XmlError> errors, boolean isResponse ) throws Exception
708 {
709 if( parts.length == 0 )
710 return;
711
712 XmlObject[] bodyParts = getRpcBodyPart(bindingOperation, msgXml, isResponse);
713
714 if( bodyParts.length != 1 )
715 {
716 errors.add( XmlError.forMessage("Missing message wrapper element [" +
717 wsdlContext.getDefinition().getTargetNamespace() + "@" + bindingOperation.getName()
718 + (isResponse ? "Response" : "" )));
719 }
720 else
721 {
722 XmlObject wrapper = bodyParts[0];
723
724 for (int i = 0; i < parts.length; i++)
725 {
726 Part part = parts[i];
727
728
729 if( isResponse )
730 {
731 if( WsdlUtils.isAttachmentOutputPart( part, bindingOperation ) )
732 continue;
733 }
734 else
735 {
736 if( WsdlUtils.isAttachmentInputPart( part, bindingOperation ) )
737 continue;
738 }
739
740
741 XmlObject[] children = wrapper.selectChildren( new QName( part.getName() ));
742
743
744 if( children.length != 1 )
745 {
746
747 QName elementName = part.getElementName();
748 if( elementName != null )
749 {
750 bodyParts = msgXml.selectPath( "declare namespace env='" +
751 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" +
752 "declare namespace ns='" + wsdlContext.getDefinition().getTargetNamespace() + "';" +
753 "declare namespace ns2='" + elementName.getNamespaceURI() + "';" +
754 "$this/env:Envelope/env:Body/ns:" + bindingOperation.getName() + (isResponse ? "Response" : "" ) +
755 "/ns2:" + elementName.getLocalPart() );
756
757 if( bodyParts.length == 1 )
758 {
759 SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement( elementName );
760 if( elm != null )
761 {
762 validateMessageBody(errors, elm.getType(), bodyParts[0]);
763 }
764 else errors.add( XmlError.forMessage("Missing part type in associated schema for [" + elementName + "]" ) );
765 }
766 else errors.add( XmlError.forMessage("Missing message part with name [" + elementName + "]" ));
767 }
768 else
769 {
770 errors.add( XmlError.forMessage("Missing message part [" + part.getName() + "]" ));
771 }
772 }
773 else
774 {
775 QName typeName = part.getTypeName();
776 SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
777 if( type != null )
778 {
779 validateMessageBody( errors, type, children[0]);
780 }
781 else errors.add( XmlError.forMessage("Missing type in associated schema for part [" + part.getName() + "]" ));
782 }
783 }
784 }
785 }
786
787 private XmlObject[] getRpcBodyPart(BindingOperation bindingOperation, XmlObject msgXml, boolean isResponse) throws Exception
788 {
789
790 String ns = WsdlUtils.getSoapBodyNamespace( isResponse ?
791 bindingOperation.getBindingOutput().getExtensibilityElements() :
792 bindingOperation.getBindingInput().getExtensibilityElements() );
793
794 if( ns == null || ns.trim().length() == 0 )
795 ns = wsdlContext.getDefinition().getTargetNamespace();
796
797
798 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" + wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" +
799 "declare namespace ns='" + ns + "';" + "$this/env:Envelope/env:Body/ns:" +
800 bindingOperation.getName() + (isResponse ? "Response" : "" ));
801 return paths;
802 }
803
804 @SuppressWarnings("unchecked")
805 private void validateSoapFault(BindingOperation bindingOperation, XmlObject msgXml, List<XmlError> errors) throws Exception
806 {
807 Map faults = bindingOperation.getBindingFaults();
808 Iterator<BindingFault> i = faults.values().iterator();
809
810 while( i.hasNext() )
811 {
812 BindingFault bindingFault = i.next();
813 String faultName = bindingFault.getName();
814
815 Part[] faultParts = WsdlUtils.getFaultParts( bindingOperation, faultName );
816 if( faultParts.length == 0 )
817 {
818 log.warn( "Missing fault parts in wsdl for fault [" + faultName + "] in bindingOperation [" + bindingOperation.getName() + "]" );
819 continue;
820 }
821
822 if( faultParts.length != 1 )
823 {
824 log.info( "Too many fault parts in wsdl for fault [" + faultName + "] in bindingOperation [" + bindingOperation.getName() + "]" );
825 continue;
826 }
827
828 Part part = faultParts[0];
829 QName elementName = part.getElementName();
830
831 if( elementName != null )
832 {
833 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" +
834 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "'; declare namespace flt='" +
835 wsdlContext.getSoapVersion().getFaultDetailNamespace() + "';" +
836 "declare namespace ns='" + elementName.getNamespaceURI() + "';" +
837 "//env:Fault/flt:detail/ns:" + elementName.getLocalPart() );
838
839 if( paths.length == 1 )
840 {
841 SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement( elementName );
842 if( elm != null )
843 {
844 validateMessageBody( errors, elm.getType(), paths[0]);
845 }
846 else errors.add( XmlError.forMessage("Missing fault part element [" + elementName + "] for fault [" +
847 part.getName() + "] in associated schema") );
848
849 return;
850 }
851 }
852
853 else if( part.getTypeName() != null )
854 {
855 QName typeName = part.getTypeName();
856
857 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" +
858 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "'; declare namespace flt='" +
859 wsdlContext.getSoapVersion().getFaultDetailNamespace() + "';" +
860 "declare namespace ns='" + typeName.getNamespaceURI() + "';" +
861 "//env:Fault/flt:detail/ns:" + part.getName() );
862
863 if( paths.length == 1 )
864 {
865 SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
866 if( type != null )
867 {
868 validateMessageBody( errors, type, paths[0]);
869 }
870 else errors.add( XmlError.forMessage("Missing fault part type [" + typeName + "] for fault [" +
871 part.getName() + "] in associated schema") );
872
873 return;
874 }
875 }
876 }
877
878
879 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" +
880 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "'; declare namespace flt='" +
881 wsdlContext.getSoapVersion().getFaultDetailNamespace() + "';//env:Fault/flt:detail" );
882
883 if( paths.length == 0 )
884 log.warn( "Missing matching Fault in wsdl for bindingOperation [" + bindingOperation.getName() + "]" );
885 else
886 {
887 String xmlText = paths[0].xmlText( new XmlOptions().setSaveOuter() );
888 log.warn( "Missing matching Fault in wsdl for Fault Detail element [" + XmlUtils.removeUnneccessaryNamespaces( xmlText ) +
889 "] in bindingOperation [" + bindingOperation.getName() + "]" );
890 }
891 }
892 }