View Javadoc

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