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