/*
* soapUI, copyright (C) 2004-2007 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.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 com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.wsdl.WsdlOperation;
import com.eviware.soapui.impl.wsdl.submit.WsdlMessageExchange;
import com.eviware.soapui.impl.wsdl.teststeps.assertions.AssertionError;
import com.eviware.soapui.model.iface.Attachment;
import com.eviware.soapui.settings.WsdlSettings;
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;
}
@SuppressWarnings("unchecked")
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();
String operationName = operation.getBindingOperationName();
BindingOperation bindingOperation = findBindingOperation(operationName);
if (bindingOperation == null)
{
errors.add(XmlError.forMessage("Missing operation ["
+ operationName + "] in wsdl definition"));
}
else
{
Part[] inputParts = WsdlUtils.getInputParts(bindingOperation);
validateMessage(messageExchange, requestContent, bindingOperation, inputParts, errors, false);
// validateInputAttachments(request, errors, bindingOperation, inputParts);
}
}
}
catch( XmlException e )
{
errors.addAll( e.getErrors() );
errors.add( XmlError.forMessage( e.getMessage() ));
}
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 );
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='" + wsdlContext.getDefinition().getTargetNamespace() + "';" +
"$this/env:Envelope/env:Body/ns:" + bindingOperation.getName() + (isResponse ? "Response" : ""));
if( paths.length != 1 )
{
throw new Exception("Missing message wrapper element [" +
wsdlContext.getDefinition().getTargetNamespace() + "@" + 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", "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);
}
catch( XmlException e )
{
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[] 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 )
{
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() );
BindingOperation bindingOperation = port.getBinding().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() );
BindingOperation bindingOperation = binding.getBindingOperation( operationName, null, null );
if( bindingOperation != null ) return bindingOperation;
}
return null;
}
@SuppressWarnings("unchecked")
public AssertionError [] assertResponse( WsdlMessageExchange messageExchange, boolean envelopeOnly )
{
List<XmlError> errors = new ArrayList<XmlError>();
try
{
String response = messageExchange.getResponseContent();
wsdlContext.getSoapVersion().validateSoapEnvelope(response, errors);
if (errors.isEmpty() && !envelopeOnly )
{
WsdlOperation operation = messageExchange.getOperation();
String operationName = operation.getBindingOperationName();
BindingOperation bindingOperation = findBindingOperation(operationName);
if (bindingOperation == null)
{
errors.add(XmlError.forMessage("Missing operation ["
+ operationName + "] in wsdl definition"));
}
else
{
Part[] outputParts = WsdlUtils.getOutputParts(bindingOperation);
validateMessage(messageExchange, response, bindingOperation, outputParts, errors, true);
}
}
}
catch ( XmlException e )
{
errors.addAll( e.getErrors() );
errors.add( XmlError.forMessage( e.getMessage() ));
}
catch (Exception e)
{
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]);
}
else errors.add( XmlError.forMessage("Missing part type 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);
String xmlText = msg.copy().changeType( type ).xmlText( xmlOptions.setSaveOuter());
XmlObject obj = type.getTypeSystem().parse( xmlText, type, xmlOptions );
obj = obj.changeType( type );
// create internal error list
List list = new ArrayList();
xmlOptions = new XmlOptions();
xmlOptions.setErrorListener( list );
xmlOptions.setValidateTreatLaxAsSkip();
obj.validate( xmlOptions );
// 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.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 [" +
wsdlContext.getDefinition().getTargetNamespace() + "@" + 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 = wsdlContext.getDefinition().getTargetNamespace();
// 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();
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 ns='" + elementName.getNamespaceURI() + "';" +
"//env:Fault/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 ns='" + typeName.getNamespaceURI() + "';" +
"//env:Fault/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() + "';" +
"//env:Fault/detail" );
if( paths.length == 0 )
log.warn( "Missing matching Fault in wsdl for bindingOperation [" + bindingOperation.getName() + "]" );
else
log.warn( "Missing matching Fault in wsdl for Fault Detail element [" +
XmlUtils.getFirstChildElement( ( Element ) paths[0].getDomNode() ).getNodeName() +
"] in bindingOperation [" + bindingOperation.getName() + "]" );
}
}
|