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