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