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
339 log.error( "Missing element [" + typeName + "] in associated schema for part [" + part.getName() + "]" );
340 }
341 else
342 {
343 SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
344 if( type != null )
345 {
346 result.add( children[0].copy().changeType( type ) );
347 }
348 else
349 log.error( "Missing type [" + typeName + "] in associated schema for part [" + part.getName() + "]" );
350 }
351 }
352 }
353 }
354 }
355 else
356 {
357 Part part = parts[0];
358 QName elementName = part.getElementName();
359 if( elementName != null )
360 {
361
362 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" +
363 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" +
364 "declare namespace ns='" + elementName.getNamespaceURI() + "';" +
365 "$this/env:Envelope/env:Body/ns:" + elementName.getLocalPart() );
366
367 if( paths.length == 1 )
368 {
369 SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement( elementName );
370 if( elm != null )
371 {
372 result.add( paths[0].copy().changeType( elm.getType() ) );
373 }
374 else throw new Exception( "Missing part type in associated schema" );
375 }
376 else throw new Exception( "Missing message part with name [" + elementName + "]" );
377 }
378 }
379
380 return result.toArray( new XmlObject[result.size()] );
381 }
382
383 @SuppressWarnings( "unchecked" )
384 public void validateXml( String request, List<XmlError> errors )
385 {
386 try
387 {
388 XmlOptions xmlOptions = new XmlOptions();
389 xmlOptions.setLoadLineNumbers();
390 xmlOptions.setErrorListener( errors );
391 xmlOptions.setLoadLineNumbers( XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT );
392 XmlObject.Factory.parse( request, xmlOptions );
393 }
394 catch( XmlException e )
395 {
396 if( e.getErrors() != null )
397 errors.addAll( e.getErrors() );
398 errors.add( XmlError.forMessage( e.getMessage() ) );
399 }
400 catch( Exception e )
401 {
402 errors.add( XmlError.forMessage( e.getMessage() ) );
403 }
404 }
405
406 private AssertionError[] convertErrors( List<XmlError> errors )
407 {
408 if( errors.size() > 0 )
409 {
410 List<AssertionError> response = new ArrayList<AssertionError>();
411 for( Iterator<XmlError> i = errors.iterator(); i.hasNext(); )
412 {
413 XmlError error = i.next();
414
415 if( error instanceof XmlValidationError )
416 {
417 XmlValidationError e = ( (XmlValidationError) error );
418 QName offendingQName = e.getOffendingQName();
419 if( offendingQName != null )
420 {
421 if( offendingQName.equals( new QName( wsdlContext.getSoapVersion().getEnvelopeNamespace(), "encodingStyle" ) ) )
422 {
423 log.debug( "ignoring encodingStyle validation.." );
424 continue;
425 }
426 else if( offendingQName.equals( new QName( wsdlContext.getSoapVersion().getEnvelopeNamespace(), "mustUnderstand" ) ) )
427 {
428 log.debug( "ignoring mustUnderstand validation.." );
429 continue;
430 }
431 }
432 }
433
434 AssertionError assertionError = new AssertionError( error );
435 if( !response.contains( assertionError ) )
436 response.add( assertionError );
437 }
438
439 return response.toArray( new AssertionError[response.size()] );
440 }
441
442 return new AssertionError[0];
443 }
444
445 @SuppressWarnings( "unchecked" )
446 public void validateMessage( WsdlMessageExchange messageExchange, String message, BindingOperation bindingOperation, Part[] parts, List<XmlError> errors, boolean isResponse )
447 {
448 try
449 {
450 if( !wsdlContext.hasSchemaTypes() )
451 {
452 errors.add( XmlError.forMessage( "Missing schema types for message" ) );
453 }
454 else
455 {
456 if( !WsdlUtils.isOutputSoapEncoded( bindingOperation ) )
457 {
458 XmlOptions xmlOptions = new XmlOptions();
459 xmlOptions.setLoadLineNumbers();
460 xmlOptions.setLoadLineNumbers( XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT );
461 XmlObject xml = XmlObject.Factory.parse( message, xmlOptions );
462
463 XmlObject[] paths = xml.selectPath( "declare namespace env='" +
464 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" +
465 "$this/env:Envelope/env:Body/env:Fault" );
466
467 if( paths.length > 0 )
468 {
469 validateSoapFault( bindingOperation, paths[0], errors );
470 }
471 else if( WsdlUtils.isRpc( wsdlContext.getDefinition(), bindingOperation ) )
472 {
473 validateRpcLiteral( bindingOperation, parts, xml, errors, isResponse );
474 }
475 else
476 {
477 validateDocLiteral( bindingOperation, parts, xml, errors, isResponse );
478 }
479
480 if( isResponse )
481 validateOutputAttachments( messageExchange, xml, errors, bindingOperation, parts );
482 else
483 validateInputAttachments( messageExchange, errors, bindingOperation, parts );
484 }
485 else errors.add( XmlError.forMessage( "Validation of SOAP-Encoded messages not supported" ) );
486 }
487 }
488 catch( XmlException e )
489 {
490 if( e.getErrors() != null )
491 errors.addAll( e.getErrors() );
492 errors.add( XmlError.forMessage( e.getMessage() ) );
493 }
494 catch( Exception e )
495 {
496 errors.add( XmlError.forMessage( e.getMessage() ) );
497 }
498 }
499
500 private BindingOperation findBindingOperation( String operationName ) throws Exception
501 {
502 Map<?, ?> services = wsdlContext.getDefinition().getAllServices();
503 Iterator<?> i = services.keySet().iterator();
504 while( i.hasNext() )
505 {
506 Service service = (Service) wsdlContext.getDefinition().getService( (QName) i.next() );
507 Map<?, ?> ports = service.getPorts();
508
509 Iterator<?> iterator = ports.keySet().iterator();
510 while( iterator.hasNext() )
511 {
512 Port port = (Port) service.getPort( (String) iterator.next() );
513 Binding binding = port.getBinding();
514 if( binding.getQName().equals( wsdlContext.getInterface().getBindingName() ) )
515 {
516 BindingOperation bindingOperation = binding.getBindingOperation( operationName, null, null );
517 if( bindingOperation != null ) return bindingOperation;
518 }
519 }
520 }
521
522 Map<?, ?> bindings = wsdlContext.getDefinition().getAllBindings();
523 i = bindings.keySet().iterator();
524 while( i.hasNext() )
525 {
526 Binding binding = (Binding) bindings.get( i.next() );
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 return null;
535 }
536
537 public AssertionError[] assertResponse( WsdlMessageExchange messageExchange, boolean envelopeOnly )
538 {
539 List<XmlError> errors = new ArrayList<XmlError>();
540 try
541 {
542 String response = messageExchange.getResponseContent();
543 wsdlContext.getSoapVersion().validateSoapEnvelope( response, errors );
544
545 if( errors.isEmpty() && !envelopeOnly )
546 {
547 WsdlOperation operation = messageExchange.getOperation();
548 BindingOperation bindingOperation = operation.getBindingOperation();
549 if( bindingOperation == null )
550 {
551 errors.add( XmlError.forMessage( "Missing operation ["
552 + operation.getBindingOperationName() + "] in wsdl definition" ) );
553 }
554 else
555 {
556 Part[] outputParts = WsdlUtils.getOutputParts( bindingOperation );
557 validateMessage( messageExchange, response, bindingOperation, outputParts, errors, true );
558 }
559 }
560 }
561 catch( Exception e )
562 {
563 e.printStackTrace();
564 errors.add( XmlError.forMessage( e.getMessage() ) );
565 }
566
567 return convertErrors( errors );
568 }
569
570 private void validateDocLiteral( BindingOperation bindingOperation, Part[] parts, XmlObject msgXml, List<XmlError> errors, boolean isResponse ) throws Exception
571 {
572 Part part = null;
573
574
575 for( int c = 0; c < parts.length; c++ )
576 {
577
578 if( ( isResponse && !WsdlUtils.isAttachmentOutputPart( parts[c], bindingOperation ) ) ||
579 ( !isResponse && !WsdlUtils.isAttachmentInputPart( parts[c], bindingOperation ) ) )
580 {
581
582 if( part != null )
583 {
584 errors.add( XmlError.forMessage( "DocLiteral message must contain 1 body part definition" ) );
585 return;
586 }
587
588 part = parts[c];
589 }
590 }
591
592 QName elementName = part.getElementName();
593 if( elementName != null )
594 {
595
596 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" +
597 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" +
598 "declare namespace ns='" + elementName.getNamespaceURI() + "';" +
599 "$this/env:Envelope/env:Body/ns:" + elementName.getLocalPart() );
600
601 if( paths.length == 1 )
602 {
603 SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement( elementName );
604 if( elm != null )
605 {
606 validateMessageBody( errors, elm.getType(), paths[0] );
607 }
608 else errors.add( XmlError.forMessage( "Missing part type [" + elementName + "] in associated schema" ) );
609 }
610 else errors.add( XmlError.forMessage( "Missing message part with name [" + elementName + "]" ) );
611 }
612 else if( part.getTypeName() != null )
613 {
614 QName typeName = part.getTypeName();
615
616 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" +
617 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" +
618 "declare namespace ns='" + typeName.getNamespaceURI() + "';" +
619 "$this/env:Envelope/env:Body/ns:" + part.getName() );
620
621 if( paths.length == 1 )
622 {
623 SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
624 if( type != null )
625 {
626 validateMessageBody( errors, type, paths[0] );
627
628
629 }
630 else errors.add( XmlError.forMessage( "Missing part type in associated schema" ) );
631 }
632 else errors.add( XmlError.forMessage( "Missing message part with name:type [" +
633 part.getName() + ":" + typeName + "]" ) );
634 }
635 }
636
637 private void validateMessageBody( List<XmlError> errors, SchemaType type, XmlObject msg ) throws XmlException
638 {
639
640
641 XmlOptions xmlOptions = new XmlOptions();
642 xmlOptions.setLoadLineNumbers();
643 xmlOptions.setLoadLineNumbers( XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT );
644
645 String xmlText = msg.copy().changeType( type ).xmlText( xmlOptions.setSaveOuter() );
646 XmlObject obj = type.getTypeSystem().parse( xmlText, type, xmlOptions );
647 obj = obj.changeType( type );
648
649
650 List<?> list = new ArrayList<Object>();
651
652 xmlOptions = new XmlOptions();
653 xmlOptions.setErrorListener( list );
654 xmlOptions.setValidateTreatLaxAsSkip();
655 obj.validate( xmlOptions );
656
657
658 for( int c = 0; c < list.size(); c++ )
659 {
660 XmlError error = (XmlError) list.get( c );
661
662 if( error instanceof XmlValidationError )
663 {
664 XmlValidationError validationError = ( (XmlValidationError) error );
665
666 if( wsdlContext.getSoapVersion().shouldIgnore( validationError ) )
667 continue;
668
669
670 if( validationError.getErrorCode().equals( "base64Binary" ) || validationError.getErrorCode().equals( "hexBinary" ) )
671 {
672 XmlCursor cursor = validationError.getCursorLocation();
673 if( cursor.toParent() )
674 {
675 String text = cursor.getTextValue();
676
677
678 if( text.startsWith( "cid:" ) || text.startsWith( "file:" ) )
679 {
680
681 continue;
682 }
683 }
684 }
685 }
686
687 int line = error.getLine() == -1 ? 0 : error.getLine() - 1;
688 errors.add( XmlError.forLocation( error.getMessage(), error.getSourceName(),
689 getLine( msg ) + line, error.getColumn(), error.getOffset() ) );
690 }
691 }
692
693 private int getLine( XmlObject object )
694 {
695 List<?> list = new ArrayList<Object>();
696 object.newCursor().getAllBookmarkRefs( list );
697 for( int c = 0; c < list.size(); c++ )
698 {
699 if( list.get( c ) instanceof XmlLineNumber )
700 {
701 return ( (XmlLineNumber) list.get( c ) ).getLine();
702 }
703 }
704
705 return -1;
706 }
707
708 private void validateRpcLiteral( BindingOperation bindingOperation, Part[] parts, XmlObject msgXml, List<XmlError> errors, boolean isResponse ) throws Exception
709 {
710 if( parts.length == 0 )
711 return;
712
713 XmlObject[] bodyParts = getRpcBodyPart( bindingOperation, msgXml, isResponse );
714
715 if( bodyParts.length != 1 )
716 {
717 errors.add( XmlError.forMessage( "Missing message wrapper element [" +
718 wsdlContext.getDefinition().getTargetNamespace() + "@" + bindingOperation.getName()
719 + ( isResponse ? "Response" : "" ) ) );
720 }
721 else
722 {
723 XmlObject wrapper = bodyParts[0];
724
725 for( int i = 0; i < parts.length; i++ )
726 {
727 Part part = parts[i];
728
729
730 if( isResponse )
731 {
732 if( WsdlUtils.isAttachmentOutputPart( part, bindingOperation ) )
733 continue;
734 }
735 else
736 {
737 if( WsdlUtils.isAttachmentInputPart( part, bindingOperation ) )
738 continue;
739 }
740
741
742 XmlObject[] children = wrapper.selectChildren( new QName( part.getName() ) );
743
744
745 if( children.length != 1 )
746 {
747
748 QName elementName = part.getElementName();
749 if( elementName != null )
750 {
751 bodyParts = msgXml.selectPath( "declare namespace env='" +
752 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" +
753 "declare namespace ns='" + wsdlContext.getDefinition().getTargetNamespace() + "';" +
754 "declare namespace ns2='" + elementName.getNamespaceURI() + "';" +
755 "$this/env:Envelope/env:Body/ns:" + bindingOperation.getName() + ( isResponse ? "Response" : "" ) +
756 "/ns2:" + elementName.getLocalPart() );
757
758 if( bodyParts.length == 1 )
759 {
760 SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement( elementName );
761 if( elm != null )
762 {
763 validateMessageBody( errors, elm.getType(), bodyParts[0] );
764 }
765 else
766 errors.add( XmlError.forMessage( "Missing part type in associated schema for [" + elementName + "]" ) );
767 }
768 else errors.add( XmlError.forMessage( "Missing message part with name [" + elementName + "]" ) );
769 }
770 else
771 {
772 errors.add( XmlError.forMessage( "Missing message part [" + part.getName() + "]" ) );
773 }
774 }
775 else
776 {
777 QName typeName = part.getTypeName();
778 SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
779 if( type != null )
780 {
781 validateMessageBody( errors, type, children[0] );
782 }
783 else
784 errors.add( XmlError.forMessage( "Missing type in associated schema for part [" + part.getName() + "]" ) );
785 }
786 }
787 }
788 }
789
790 private XmlObject[] getRpcBodyPart( BindingOperation bindingOperation, XmlObject msgXml, boolean isResponse ) throws Exception
791 {
792
793 String ns = WsdlUtils.getSoapBodyNamespace( isResponse ?
794 bindingOperation.getBindingOutput().getExtensibilityElements() :
795 bindingOperation.getBindingInput().getExtensibilityElements() );
796
797 if( ns == null || ns.trim().length() == 0 )
798 ns = wsdlContext.getDefinition().getTargetNamespace();
799
800
801 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" + wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" +
802 "declare namespace ns='" + ns + "';" + "$this/env:Envelope/env:Body/ns:" +
803 bindingOperation.getName() + ( isResponse ? "Response" : "" ) );
804 return paths;
805 }
806
807 @SuppressWarnings( "unchecked" )
808 private void validateSoapFault( BindingOperation bindingOperation, XmlObject msgXml, List<XmlError> errors ) throws Exception
809 {
810 Map faults = bindingOperation.getBindingFaults();
811 Iterator<BindingFault> i = faults.values().iterator();
812
813
814 List<?> list = new ArrayList<Object>();
815
816 XmlOptions xmlOptions = new XmlOptions();
817 xmlOptions.setErrorListener( list );
818 xmlOptions.setValidateTreatLaxAsSkip();
819 msgXml.validate( xmlOptions );
820
821 for( Object o : list )
822 {
823 if( o instanceof XmlError )
824 errors.add( (XmlError) o );
825 else
826 errors.add( XmlError.forMessage( o.toString() ) );
827 }
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
850 if( elementName != null )
851 {
852 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" +
853 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "'; declare namespace flt='" +
854 wsdlContext.getSoapVersion().getFaultDetailNamespace() + "';" +
855 "declare namespace ns='" + elementName.getNamespaceURI() + "';" +
856 "//env:Fault/flt:detail/ns:" + elementName.getLocalPart() );
857
858 if( paths.length == 1 )
859 {
860 SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement( elementName );
861 if( elm != null )
862 {
863 validateMessageBody( errors, elm.getType(), paths[0] );
864 }
865 else errors.add( XmlError.forMessage( "Missing fault part element [" + elementName + "] for fault [" +
866 part.getName() + "] in associated schema" ) );
867
868 return;
869 }
870 }
871
872 else if( part.getTypeName() != null )
873 {
874 QName typeName = part.getTypeName();
875
876 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" +
877 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "'; declare namespace flt='" +
878 wsdlContext.getSoapVersion().getFaultDetailNamespace() + "';" +
879 "declare namespace ns='" + typeName.getNamespaceURI() + "';" +
880 "//env:Fault/flt:detail/ns:" + part.getName() );
881
882 if( paths.length == 1 )
883 {
884 SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
885 if( type != null )
886 {
887 validateMessageBody( errors, type, paths[0] );
888 }
889 else errors.add( XmlError.forMessage( "Missing fault part type [" + typeName + "] for fault [" +
890 part.getName() + "] in associated schema" ) );
891
892 return;
893 }
894 }
895 }
896
897
898 XmlObject[] paths = msgXml.selectPath( "declare namespace env='" +
899 wsdlContext.getSoapVersion().getEnvelopeNamespace() + "'; declare namespace flt='" +
900 wsdlContext.getSoapVersion().getFaultDetailNamespace() + "';//env:Fault/flt:detail" );
901
902 if( paths.length == 0 )
903 log.warn( "Missing matching Fault in wsdl for bindingOperation [" + bindingOperation.getName() + "]" );
904 else
905 {
906 String xmlText = paths[0].xmlText( new XmlOptions().setSaveOuter() );
907 log.warn( "Missing matching Fault in wsdl for Fault Detail element [" + XmlUtils.removeUnneccessaryNamespaces( xmlText ) +
908 "] in bindingOperation [" + bindingOperation.getName() + "]" );
909 }
910 }
911 }