Package com.eviware.soapui.impl.wsdl.support.wsdl

Source Code of com.eviware.soapui.impl.wsdl.support.wsdl.WsdlValidator

/*
*  soapUI, copyright (C) 2004-2011 eviware.com
*
*  soapUI is free software; you can redistribute it and/or modify it under the
*  terms of version 2.1 of the GNU Lesser General Public License as published by
*  the Free Software Foundation.
*
*  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
*  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*  See the GNU Lesser General Public License for more details at gnu.org.
*/

package com.eviware.soapui.impl.wsdl.support.wsdl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.wsdl.Binding;
import javax.wsdl.BindingFault;
import javax.wsdl.BindingOperation;
import javax.wsdl.Part;
import javax.wsdl.Port;
import javax.wsdl.Service;
import javax.wsdl.extensions.mime.MIMEContent;
import javax.xml.namespace.QName;

import org.apache.log4j.Logger;
import org.apache.xmlbeans.SchemaGlobalElement;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlError;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlLineNumber;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlValidationError;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.wsdl.WsdlOperation;
import com.eviware.soapui.impl.wsdl.submit.WsdlMessageExchange;
import com.eviware.soapui.model.iface.Attachment;
import com.eviware.soapui.model.testsuite.AssertionError;
import com.eviware.soapui.settings.WsdlSettings;
import com.eviware.soapui.support.StringUtils;
import com.eviware.soapui.support.xml.XmlUtils;

/**
* Class for validating SOAP requests/responses against their definition and
* schema, requires that the messages follow basic-profile requirements
*
* @author Ole.Matzura
*/

public class WsdlValidator
{
  private final WsdlContext wsdlContext;
  private final static Logger log = Logger.getLogger( WsdlValidator.class );

  public WsdlValidator( WsdlContext wsdlContext )
  {
    this.wsdlContext = wsdlContext;
  }

  public AssertionError[] assertRequest( WsdlMessageExchange messageExchange, boolean envelopeOnly )
  {
    List<XmlError> errors = new ArrayList<XmlError>();
    try
    {
      String requestContent = messageExchange.getRequestContent();
      wsdlContext.getSoapVersion().validateSoapEnvelope( requestContent, errors );

      if( errors.isEmpty() && !envelopeOnly )
      {
        wsdlContext.getSoapVersion().validateSoapEnvelope( requestContent, errors );
        WsdlOperation operation = messageExchange.getOperation();
        BindingOperation bindingOperation = operation.getBindingOperation();
        if( bindingOperation == null )
        {
          errors.add( XmlError.forMessage( "Missing operation [" + operation.getBindingOperationName()
              + "] in wsdl definition" ) );
        }
        else
        {
          Part[] inputParts = WsdlUtils.getInputParts( bindingOperation );
          validateMessage( messageExchange, requestContent, bindingOperation, inputParts, errors, false );
          // validateInputAttachments(request, errors, bindingOperation,
          // inputParts);
        }
      }
    }
    catch( Exception e )
    {
      errors.add( XmlError.forMessage( e.getMessage() ) );
    }

    return convertErrors( errors );
  }

  private void validateInputAttachments( WsdlMessageExchange messageExchange, List<XmlError> errors,
      BindingOperation bindingOperation, Part[] inputParts )
  {
    for( Part part : inputParts )
    {
      MIMEContent[] contents = WsdlUtils.getInputMultipartContent( part, bindingOperation );
      if( contents.length == 0 )
        continue;

      Attachment[] attachments = messageExchange.getRequestAttachmentsForPart( part.getName() );
      if( attachments.length == 0 )
      {
        errors.add( XmlError.forMessage( "Missing attachment for part [" + part.getName() + "]" ) );
      }
      else if( attachments.length == 1 )
      {
        Attachment attachment = attachments[0];
        String types = "";
        for( MIMEContent content : contents )
        {
          String type = content.getType();
          if( type.equals( attachment.getContentType() ) || type.toUpperCase().startsWith( "MULTIPART" ) )
          {
            types = null;
            break;
          }
          if( types.length() > 0 )
            types += ",";

          types += type;
        }

        if( types != null )
        {
          String msg = "Missing attachment for part [" + part.getName() + "] with content-type [" + types + "],"
              + " content type is [" + attachment.getContentType() + "]";

          if( SoapUI.getSettings().getBoolean( WsdlSettings.ALLOW_INCORRECT_CONTENTTYPE ) )
            log.warn( msg );
          else
            errors.add( XmlError.forMessage( msg ) );
        }
      }
      else
      {
        String types = "";
        for( MIMEContent content : contents )
        {
          String type = content.getType();
          if( type.toUpperCase().startsWith( "MULTIPART" ) )
          {
            types = null;
            break;
          }
          if( types.length() > 0 )
            types += ",";

          types += type;
        }

        if( types == null )
        {
          String msg = "Too many attachments for part [" + part.getName() + "] with content-type [" + types + "]";
          if( SoapUI.getSettings().getBoolean( WsdlSettings.ALLOW_INCORRECT_CONTENTTYPE ) )
            log.warn( msg );
          else
            errors.add( XmlError.forMessage( msg ) );
        }
      }

      if( attachments.length > 0 )
        validateAttachmentsReadability( errors, attachments );
    }
  }

  private void validateOutputAttachments( WsdlMessageExchange messageExchange, XmlObject xml, List<XmlError> errors,
      BindingOperation bindingOperation, Part[] outputParts ) throws Exception
  {
    for( Part part : outputParts )
    {
      MIMEContent[] contents = WsdlUtils.getOutputMultipartContent( part, bindingOperation );
      if( contents.length == 0 )
        continue;

      Attachment[] attachments = messageExchange.getResponseAttachmentsForPart( part.getName() );

      // check for rpc
      if( attachments.length == 0 && WsdlUtils.isRpc( wsdlContext.getDefinition(), bindingOperation ) )
      {
        XmlObject[] rpcBodyPart = getRpcBodyPart( bindingOperation, xml, true );
        if( rpcBodyPart.length == 1 )
        {
          XmlObject[] children = rpcBodyPart[0].selectChildren( new QName( part.getName() ) );
          if( children.length == 1 )
          {
            String href = ( ( Element )children[0].getDomNode() ).getAttribute( "href" );
            if( href != null )
            {
              if( href.startsWith( "cid:" ) )
                href = href.substring( 4 );

              attachments = messageExchange.getResponseAttachmentsForPart( href );
            }
          }
        }
      }

      if( attachments.length == 0 )
      {
        errors.add( XmlError.forMessage( "Missing attachment for part [" + part.getName() + "]" ) );
      }
      else if( attachments.length == 1 )
      {
        Attachment attachment = attachments[0];
        String types = "";
        for( MIMEContent content : contents )
        {
          String type = content.getType();
          if( type.equals( attachment.getContentType() ) || type.toUpperCase().startsWith( "MULTIPART" ) )
          {
            types = null;
            break;
          }

          if( types.length() > 0 )
            types += ",";

          types += type;
        }

        if( types != null )
        {
          String msg = "Missing attachment for part [" + part.getName() + "] with content-type [" + types
              + "], content type is [" + attachment.getContentType() + "]";

          if( SoapUI.getSettings().getBoolean( WsdlSettings.ALLOW_INCORRECT_CONTENTTYPE ) )
            log.warn( msg );
          else
            errors.add( XmlError.forMessage( msg ) );
        }
      }
      else
      {
        String types = "";
        for( MIMEContent content : contents )
        {
          String type = content.getType();
          if( type.toUpperCase().startsWith( "MULTIPART" ) )
          {
            types = null;
            break;
          }

          if( types.length() > 0 )
            types += ",";

          types += type;
        }

        if( types != null )
        {
          String msg = "Too many attachments for part [" + part.getName() + "] with content-type [" + types + "]";

          if( SoapUI.getSettings().getBoolean( WsdlSettings.ALLOW_INCORRECT_CONTENTTYPE ) )
            log.warn( msg );
          else
            errors.add( XmlError.forMessage( msg ) );
        }
      }

      if( attachments.length > 0 )
        validateAttachmentsReadability( errors, attachments );
    }
  }

  private void validateAttachmentsReadability( List<XmlError> errors, Attachment[] attachments )
  {
    for( Attachment attachment : attachments )
    {
      try
      {
        attachment.getInputStream();
      }
      catch( Exception e )
      {
        errors.add( XmlError.forMessage( e.toString() ) );
      }
    }
  }

  public XmlObject[] getMessageParts( String messageContent, String operationName, boolean isResponse )
      throws Exception
  {
    BindingOperation bindingOperation = findBindingOperation( operationName );
    if( bindingOperation == null )
    {
      throw new Exception( "Missing operation [" + operationName + "] in wsdl definition" );
    }

    if( !wsdlContext.hasSchemaTypes() )
    {
      throw new Exception( "Missing schema types for message" );
    }

    // XmlObject msgXml = XmlObject.Factory.parse( messageContent );
    XmlObject msgXml = XmlUtils.createXmlObject( messageContent );
    Part[] parts = isResponse ? WsdlUtils.getOutputParts( bindingOperation ) : WsdlUtils
        .getInputParts( bindingOperation );
    if( parts == null || parts.length == 0 )
      throw new Exception( "Missing parts for operation [" + operationName + "]" );

    List<XmlObject> result = new ArrayList<XmlObject>();

    if( WsdlUtils.isRpc( wsdlContext.getDefinition(), bindingOperation ) )
    {
      // get root element
      XmlObject[] paths = msgXml.selectPath( "declare namespace env='"
          + wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" + "declare namespace ns='"
          + WsdlUtils.getTargetNamespace( wsdlContext.getDefinition() ) + "';" + "$this/env:Envelope/env:Body/ns:"
          + bindingOperation.getName() + ( isResponse ? "Response" : "" ) );

      if( paths.length != 1 )
      {
        throw new Exception( "Missing message wrapper element ["
            + WsdlUtils.getTargetNamespace( wsdlContext.getDefinition() ) + "@" + bindingOperation.getName()
            + ( isResponse ? "Response]" : "]" ) );
      }
      else
      {
        XmlObject wrapper = paths[0];

        for( int i = 0; i < parts.length; i++ )
        {
          Part part = parts[i];
          if( ( isResponse && WsdlUtils.isAttachmentOutputPart( part, bindingOperation ) )
              || ( !isResponse && WsdlUtils.isAttachmentInputPart( part, bindingOperation ) ) )
            continue;

          QName partName = part.getElementName();
          if( partName == null )
            partName = new QName( part.getName() );

          XmlObject[] children = wrapper.selectChildren( partName );
          if( children.length != 1 )
          {
            log.error( "Missing message part [" + part.getName() + "]" );
          }
          else
          {
            QName typeName = part.getTypeName();
            if( typeName == null )
            {
              typeName = partName;
              SchemaGlobalElement type = wsdlContext.getSchemaTypeLoader().findElement( typeName );

              if( type != null )
              {
                result.add( children[0].copy().changeType( type.getType() ) );
              }
              else
                log.error( "Missing element [" + typeName + "] in associated schema for part ["
                    + part.getName() + "]" );
            }
            else
            {
              SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
              if( type != null )
              {
                result.add( children[0].copy().changeType( type ) );
              }
              else
                log.error( "Missing type [" + typeName + "] in associated schema for part [" + part.getName()
                    + "]" );
            }
          }
        }
      }
    }
    else
    {
      Part part = parts[0];
      QName elementName = part.getElementName();
      if( elementName != null )
      {
        // just check for correct message element, other elements are
        // avoided (should create an error)
        XmlObject[] paths = msgXml.selectPath( "declare namespace env='"
            + wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" + "declare namespace ns='"
            + elementName.getNamespaceURI() + "';" + "$this/env:Envelope/env:Body/ns:"
            + elementName.getLocalPart() );

        if( paths.length == 1 )
        {
          SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement( elementName );
          if( elm != null )
          {
            result.add( paths[0].copy().changeType( elm.getType() ) );
          }
          else
            throw new Exception( "Missing part type in associated schema" );
        }
        else
          throw new Exception( "Missing message part with name [" + elementName + "]" );
      }
    }

    return result.toArray( new XmlObject[result.size()] );
  }

  @SuppressWarnings( "unchecked" )
  public void validateXml( String request, List<XmlError> errors )
  {
    try
    {
      XmlOptions xmlOptions = new XmlOptions();
      xmlOptions.setLoadLineNumbers();
      xmlOptions.setErrorListener( errors );
      xmlOptions.setLoadLineNumbers( XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT );
      // XmlObject.Factory.parse( request, xmlOptions );
      XmlUtils.createXmlObject( request, xmlOptions );
    }
    catch( XmlException e )
    {
      if( e.getErrors() != null )
        errors.addAll( e.getErrors() );
      errors.add( XmlError.forMessage( e.getMessage() ) );
    }
    catch( Exception e )
    {
      errors.add( XmlError.forMessage( e.getMessage() ) );
    }
  }

  private AssertionError[] convertErrors( List<XmlError> errors )
  {
    if( errors.size() > 0 )
    {
      List<AssertionError> response = new ArrayList<AssertionError>();
      for( Iterator<XmlError> i = errors.iterator(); i.hasNext(); )
      {
        XmlError error = i.next();

        if( error instanceof XmlValidationError )
        {
          XmlValidationError e = ( ( XmlValidationError )error );
          QName offendingQName = e.getOffendingQName();
          if( offendingQName != null )
          {
            if( offendingQName.equals( new QName( wsdlContext.getSoapVersion().getEnvelopeNamespace(),
                "encodingStyle" ) ) )
            {
              log.debug( "ignoring encodingStyle validation.." );
              continue;
            }
            else if( offendingQName.equals( new QName( wsdlContext.getSoapVersion().getEnvelopeNamespace(),
                "mustUnderstand" ) ) )
            {
              log.debug( "ignoring mustUnderstand validation.." );
              continue;
            }
          }
        }

        AssertionError assertionError = new AssertionError( error );
        if( !response.contains( assertionError ) )
          response.add( assertionError );
      }

      return response.toArray( new AssertionError[response.size()] );
    }

    return new AssertionError[0];
  }

  @SuppressWarnings( "unchecked" )
  public void validateMessage( WsdlMessageExchange messageExchange, String message, BindingOperation bindingOperation,
      Part[] parts, List<XmlError> errors, boolean isResponse )
  {
    try
    {
      if( !wsdlContext.hasSchemaTypes() )
      {
        errors.add( XmlError.forMessage( "Missing schema types for message" ) );
      }
      else
      {
        if( !WsdlUtils.isOutputSoapEncoded( bindingOperation ) )
        {
          XmlOptions xmlOptions = new XmlOptions();
          xmlOptions.setLoadLineNumbers();
          xmlOptions.setLoadLineNumbers( XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT );
          // XmlObject xml = XmlObject.Factory.parse( message, xmlOptions
          // );
          XmlObject xml = XmlUtils.createXmlObject( message, xmlOptions );

          XmlObject[] paths = xml.selectPath( "declare namespace env='"
              + wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';"
              + "$this/env:Envelope/env:Body/env:Fault" );

          if( paths.length > 0 )
          {
            validateSoapFault( bindingOperation, paths[0], errors );
          }
          else if( WsdlUtils.isRpc( wsdlContext.getDefinition(), bindingOperation ) )
          {
            validateRpcLiteral( bindingOperation, parts, xml, errors, isResponse );
          }
          else
          {
            validateDocLiteral( bindingOperation, parts, xml, errors, isResponse );
          }

          if( isResponse )
            validateOutputAttachments( messageExchange, xml, errors, bindingOperation, parts );
          else
            validateInputAttachments( messageExchange, errors, bindingOperation, parts );
        }
        else
          errors.add( XmlError.forMessage( "Validation of SOAP-Encoded messages not supported" ) );
      }
    }
    catch( XmlException e )
    {
      if( e.getErrors() != null )
        errors.addAll( e.getErrors() );
      errors.add( XmlError.forMessage( e.getMessage() ) );
    }
    catch( Exception e )
    {
      errors.add( XmlError.forMessage( e.getMessage() ) );
    }
  }

  private BindingOperation findBindingOperation( String operationName ) throws Exception
  {
    Map<?, ?> services = wsdlContext.getDefinition().getAllServices();
    Iterator<?> i = services.keySet().iterator();
    while( i.hasNext() )
    {
      Service service = ( Service )wsdlContext.getDefinition().getService( ( QName )i.next() );
      Map<?, ?> ports = service.getPorts();

      Iterator<?> iterator = ports.keySet().iterator();
      while( iterator.hasNext() )
      {
        Port port = ( Port )service.getPort( ( String )iterator.next() );
        Binding binding = port.getBinding();
        if( binding.getQName().equals( wsdlContext.getInterface().getBindingName() ) )
        {
          BindingOperation bindingOperation = binding.getBindingOperation( operationName, null, null );
          if( bindingOperation != null )
            return bindingOperation;
        }
      }
    }

    Map<?, ?> bindings = wsdlContext.getDefinition().getAllBindings();
    i = bindings.keySet().iterator();
    while( i.hasNext() )
    {
      Binding binding = ( Binding )bindings.get( i.next() );
      if( binding.getQName().equals( wsdlContext.getInterface().getBindingName() ) )
      {
        BindingOperation bindingOperation = binding.getBindingOperation( operationName, null, null );
        if( bindingOperation != null )
          return bindingOperation;
      }
    }

    return null;
  }

  public AssertionError[] assertResponse( WsdlMessageExchange messageExchange, boolean envelopeOnly )
  {
    List<XmlError> errors = new ArrayList<XmlError>();
    try
    {
      String response = messageExchange.getResponseContent();

      if( StringUtils.isNullOrEmpty( response ) )
      {
        if( !messageExchange.getOperation().isOneWay() )
        {
          errors.add( XmlError.forMessage( "Response is missing or empty" ) );
        }
      }
      else
      {
        wsdlContext.getSoapVersion().validateSoapEnvelope( response, errors );

        if( errors.isEmpty() && !envelopeOnly )
        {
          WsdlOperation operation = messageExchange.getOperation();
          BindingOperation bindingOperation = operation.getBindingOperation();
          if( bindingOperation == null )
          {
            errors.add( XmlError.forMessage( "Missing operation [" + operation.getBindingOperationName()
                + "] in wsdl definition" ) );
          }
          else
          {
            Part[] outputParts = WsdlUtils.getOutputParts( bindingOperation );
            validateMessage( messageExchange, response, bindingOperation, outputParts, errors, true );
          }
        }
      }
    }
    catch( Exception e )
    {
      e.printStackTrace();
      errors.add( XmlError.forMessage( e.getMessage() ) );
    }

    return convertErrors( errors );
  }

  private void validateDocLiteral( BindingOperation bindingOperation, Part[] parts, XmlObject msgXml,
      List<XmlError> errors, boolean isResponse ) throws Exception
  {
    Part part = null;

    // start by finding body part
    for( int c = 0; c < parts.length; c++ )
    {
      // content part?
      if( ( isResponse && !WsdlUtils.isAttachmentOutputPart( parts[c], bindingOperation ) )
          || ( !isResponse && !WsdlUtils.isAttachmentInputPart( parts[c], bindingOperation ) ) )
      {
        // already found?
        if( part != null )
        {
          errors.add( XmlError.forMessage( "DocLiteral message must contain 1 body part definition" ) );
          return;
        }

        part = parts[c];
      }
    }

    QName elementName = part.getElementName();
    if( elementName != null )
    {
      // just check for correct message element, other elements are avoided
      // (should create an error)
      XmlObject[] paths = msgXml.selectPath( "declare namespace env='"
          + wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" + "declare namespace ns='"
          + elementName.getNamespaceURI() + "';" + "$this/env:Envelope/env:Body/ns:" + elementName.getLocalPart() );

      if( paths.length == 1 )
      {
        SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement( elementName );
        if( elm != null )
        {
          validateMessageBody( errors, elm.getType(), paths[0] );

          // ensure no other elements in body
          NodeList children = XmlUtils.getChildElements( ( Element )paths[0].getDomNode().getParentNode() );
          for( int c = 0; c < children.getLength(); c++ )
          {
            QName childName = XmlUtils.getQName( children.item( c ) );
            if( !elementName.equals( childName ) )
            {
              XmlCursor cur = paths[0].newCursor();
              cur.toParent();
              cur.toChild( childName );
              errors.add( XmlError.forCursor( "Invalid element [" + childName + "] in SOAP Body", cur ) );
              cur.dispose();
            }
          }
        }
        else
          errors.add( XmlError.forMessage( "Missing part type [" + elementName + "] in associated schema" ) );
      }
      else
        errors.add( XmlError.forMessage( "Missing message part with name [" + elementName + "]" ) );
    }
    else if( part.getTypeName() != null )
    {
      QName typeName = part.getTypeName();

      XmlObject[] paths = msgXml.selectPath( "declare namespace env='"
          + wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" + "declare namespace ns='"
          + typeName.getNamespaceURI() + "';" + "$this/env:Envelope/env:Body/ns:" + part.getName() );

      if( paths.length == 1 )
      {
        SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
        if( type != null )
        {
          validateMessageBody( errors, type, paths[0] );
          // XmlObject obj = paths[0].copy().changeType( type );
          // obj.validate( new XmlOptions().setErrorListener( errors ));
        }
        else
          errors.add( XmlError.forMessage( "Missing part type in associated schema" ) );
      }
      else
        errors.add( XmlError.forMessage( "Missing message part with name:type [" + part.getName() + ":" + typeName
            + "]" ) );
    }
  }

  private void validateMessageBody( List<XmlError> errors, SchemaType type, XmlObject msg ) throws XmlException
  {
    // need to create new body element of correct type from xml text
    // since we want to retain line-numbers
    XmlOptions xmlOptions = new XmlOptions();
    xmlOptions.setLoadLineNumbers();
    xmlOptions.setLoadLineNumbers( XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT );

    XmlCursor cur = msg.newCursor();
    Map<String, String> map = new HashMap<String, String>();

    while( cur.hasNextToken() )
    {
      if( cur.toNextToken().isNamespace() )
        map.put( cur.getName().getLocalPart(), cur.getTextValue() );
    }

    xmlOptions.setUseDefaultNamespace();
    xmlOptions.setSaveOuter();

    // problem: prefixes might get redefined/changed when saving which can
    // cause xsi:type refs to
    // reference wrong/non-existing namespace.. solution would probably be to
    // manually walk through document and
    // update xsi:type refs with new prefix. The setUseDefaultNamespace()
    // above helps here but is not a definitive fix

    String xmlText = msg.copy().changeType( type ).xmlText( xmlOptions );

    xmlOptions.setLoadAdditionalNamespaces( map );

    XmlObject obj = type.getTypeSystem().parse( xmlText, type, xmlOptions );
    obj = obj.changeType( type );

    // create internal error list
    ArrayList<Object> list = new ArrayList<Object>();

    xmlOptions = new XmlOptions();
    xmlOptions.setErrorListener( list );
    xmlOptions.setValidateTreatLaxAsSkip();

    try
    {
      obj.validate( xmlOptions );
    }
    catch( Exception e )
    {
      SoapUI.logError( e );
      list.add( "Internal Error - see error log for details - [" + e + "]" );
    }

    // transfer errors for "real" line numbers
    for( int c = 0; c < list.size(); c++ )
    {
      XmlError error = ( XmlError )list.get( c );

      if( error instanceof XmlValidationError )
      {
        XmlValidationError validationError = ( ( XmlValidationError )error );

        if( wsdlContext.getSoapVersion().shouldIgnore( validationError ) )
          continue;

        // ignore cid: related errors
        if( validationError.getErrorCode().equals( "base64Binary" )
            || validationError.getErrorCode().equals( "hexBinary" ) )
        {
          XmlCursor cursor = validationError.getCursorLocation();
          if( cursor.toParent() )
          {
            String text = cursor.getTextValue();

            // special handling for soapui/MTOM -> add option for
            // disabling?
            if( text.startsWith( "cid:" ) || text.startsWith( "file:" ) )
            {
              // ignore
              continue;
            }
          }
        }
      }

      int line = error.getLine() == -1 ? 0 : error.getLine() - 1;
      errors.add( XmlError.forLocation( error.getMessage(), error.getSourceName(), getLine( msg ) + line,
          error.getColumn(), error.getOffset() ) );
    }
  }

  private int getLine( XmlObject object )
  {
    List<?> list = new ArrayList<Object>();
    object.newCursor().getAllBookmarkRefs( list );
    for( int c = 0; c < list.size(); c++ )
    {
      if( list.get( c ) instanceof XmlLineNumber )
      {
        return ( ( XmlLineNumber )list.get( c ) ).getLine();
      }
    }

    return -1;
  }

  private void validateRpcLiteral( BindingOperation bindingOperation, Part[] parts, XmlObject msgXml,
      List<XmlError> errors, boolean isResponse ) throws Exception
  {
    if( parts.length == 0 )
      return;

    XmlObject[] bodyParts = getRpcBodyPart( bindingOperation, msgXml, isResponse );

    if( bodyParts.length != 1 )
    {
      errors.add( XmlError.forMessage( "Missing message wrapper element ["
          + WsdlUtils.getTargetNamespace( wsdlContext.getDefinition() ) + "@" + bindingOperation.getName()
          + ( isResponse ? "Response" : "" ) ) );
    }
    else
    {
      XmlObject wrapper = bodyParts[0];

      for( int i = 0; i < parts.length; i++ )
      {
        Part part = parts[i];

        // skip attachment parts
        if( isResponse )
        {
          if( WsdlUtils.isAttachmentOutputPart( part, bindingOperation ) )
            continue;
        }
        else
        {
          if( WsdlUtils.isAttachmentInputPart( part, bindingOperation ) )
            continue;
        }

        // find part in message
        XmlObject[] children = wrapper.selectChildren( new QName( part.getName() ) );

        // not found?
        if( children.length != 1 )
        {
          // try element name (loophole in basic-profile spec?)
          QName elementName = part.getElementName();
          if( elementName != null )
          {
            bodyParts = msgXml.selectPath( "declare namespace env='"
                + wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" + "declare namespace ns='"
                + wsdlContext.getDefinition().getTargetNamespace() + "';" + "declare namespace ns2='"
                + elementName.getNamespaceURI() + "';" + "$this/env:Envelope/env:Body/ns:"
                + bindingOperation.getName() + ( isResponse ? "Response" : "" ) + "/ns2:"
                + elementName.getLocalPart() );

            if( bodyParts.length == 1 )
            {
              SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement( elementName );
              if( elm != null )
              {
                validateMessageBody( errors, elm.getType(), bodyParts[0] );
              }
              else
                errors.add( XmlError.forMessage( "Missing part type in associated schema for [" + elementName
                    + "]" ) );
            }
            else
              errors.add( XmlError.forMessage( "Missing message part with name [" + elementName + "]" ) );
          }
          else
          {
            errors.add( XmlError.forMessage( "Missing message part [" + part.getName() + "]" ) );
          }
        }
        else
        {
          QName typeName = part.getTypeName();
          SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
          if( type != null )
          {
            validateMessageBody( errors, type, children[0] );
          }
          else
            errors.add( XmlError.forMessage( "Missing type in associated schema for part [" + part.getName()
                + "]" ) );
        }
      }
    }
  }

  private XmlObject[] getRpcBodyPart( BindingOperation bindingOperation, XmlObject msgXml, boolean isResponse )
      throws Exception
  {
    // rpc requests should use the operation name as root element and soapbind
    // namespaceuri attribute as ns
    String ns = WsdlUtils.getSoapBodyNamespace( isResponse ? bindingOperation.getBindingOutput()
        .getExtensibilityElements() : bindingOperation.getBindingInput().getExtensibilityElements() );

    if( ns == null || ns.trim().length() == 0 )
      ns = WsdlUtils.getTargetNamespace( wsdlContext.getDefinition() );

    // get root element
    XmlObject[] paths = msgXml.selectPath( "declare namespace env='"
        + wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" + "declare namespace ns='" + ns + "';"
        + "$this/env:Envelope/env:Body/ns:" + bindingOperation.getName() + ( isResponse ? "Response" : "" ) );
    return paths;
  }

  @SuppressWarnings( "unchecked" )
  private void validateSoapFault( BindingOperation bindingOperation, XmlObject msgXml, List<XmlError> errors )
      throws Exception
  {
    Map faults = bindingOperation.getBindingFaults();
    Iterator<BindingFault> i = faults.values().iterator();

    // create internal error list
    List<?> list = new ArrayList<Object>();

    XmlOptions xmlOptions = new XmlOptions();
    xmlOptions.setErrorListener( list );
    xmlOptions.setValidateTreatLaxAsSkip();
    msgXml.validate( xmlOptions );

    for( Object o : list )
    {
      if( o instanceof XmlError )
        errors.add( ( XmlError )o );
      else
        errors.add( XmlError.forMessage( o.toString() ) );
    }

    while( i.hasNext() )
    {
      BindingFault bindingFault = i.next();
      String faultName = bindingFault.getName();

      Part[] faultParts = WsdlUtils.getFaultParts( bindingOperation, faultName );
      if( faultParts.length == 0 )
      {
        log.warn( "Missing fault parts in wsdl for fault [" + faultName + "] in bindingOperation ["
            + bindingOperation.getName() + "]" );
        continue;
      }

      if( faultParts.length != 1 )
      {
        log.info( "Too many fault parts in wsdl for fault [" + faultName + "] in bindingOperation ["
            + bindingOperation.getName() + "]" );
        continue;
      }

      Part part = faultParts[0];
      QName elementName = part.getElementName();

      if( elementName != null )
      {
        XmlObject[] paths = msgXml.selectPath( "declare namespace env='"
            + wsdlContext.getSoapVersion().getEnvelopeNamespace() + "'; declare namespace flt='"
            + wsdlContext.getSoapVersion().getFaultDetailNamespace() + "';" + "declare namespace ns='"
            + elementName.getNamespaceURI() + "';" + "//env:Fault/flt:detail/ns:" + elementName.getLocalPart() );

        if( paths.length == 1 )
        {
          SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement( elementName );
          if( elm != null )
          {
            validateMessageBody( errors, elm.getType(), paths[0] );
          }
          else
            errors.add( XmlError.forMessage( "Missing fault part element [" + elementName + "] for fault ["
                + part.getName() + "] in associated schema" ) );

          return;
        }
      }
      // this is not allowed by Basic Profile.. remove?
      else if( part.getTypeName() != null )
      {
        QName typeName = part.getTypeName();

        XmlObject[] paths = msgXml.selectPath( "declare namespace env='"
            + wsdlContext.getSoapVersion().getEnvelopeNamespace() + "'; declare namespace flt='"
            + wsdlContext.getSoapVersion().getFaultDetailNamespace() + "';" + "declare namespace ns='"
            + typeName.getNamespaceURI() + "';" + "//env:Fault/flt:detail/ns:" + part.getName() );

        if( paths.length == 1 )
        {
          SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
          if( type != null )
          {
            validateMessageBody( errors, type, paths[0] );
          }
          else
            errors.add( XmlError.forMessage( "Missing fault part type [" + typeName + "] for fault ["
                + part.getName() + "] in associated schema" ) );

          return;
        }
      }
    }

    // if we get here, no matching fault was found.. this is not an error but
    // should be warned..
    XmlObject[] paths = msgXml.selectPath( "declare namespace env='"
        + wsdlContext.getSoapVersion().getEnvelopeNamespace() + "'; declare namespace flt='"
        + wsdlContext.getSoapVersion().getFaultDetailNamespace() + "';//env:Fault/flt:detail" );

    if( paths.length == 0 )
      log.warn( "Missing matching Fault in wsdl for bindingOperation [" + bindingOperation.getName() + "]" );
    else
    {
      String xmlText = paths[0].xmlText( new XmlOptions().setSaveOuter() );
      log.warn( "Missing matching Fault in wsdl for Fault Detail element ["
          + XmlUtils.removeUnneccessaryNamespaces( xmlText ) + "] in bindingOperation ["
          + bindingOperation.getName() + "]" );
    }
  }
}
TOP

Related Classes of com.eviware.soapui.impl.wsdl.support.wsdl.WsdlValidator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.