/*
* 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.soap;
import java.io.StringWriter;
import java.util.List;
import java.util.Map;
import javax.wsdl.BindingInput;
import javax.wsdl.BindingOperation;
import javax.wsdl.BindingOutput;
import javax.wsdl.Message;
import javax.wsdl.Part;
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.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.wsdl.WsdlInterface;
import com.eviware.soapui.impl.wsdl.support.Constants;
import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlContext;
import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils;
import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils.SoapHeader;
import com.eviware.soapui.impl.wsdl.support.xsd.SampleXmlUtil;
import com.eviware.soapui.model.iface.Interface;
import com.eviware.soapui.model.iface.MessageBuilder;
import com.eviware.soapui.model.iface.MessagePart.FaultPart;
import com.eviware.soapui.settings.WsdlSettings;
import com.eviware.soapui.support.xml.XmlUtils;
/**
* Builds SOAP requests according to WSDL/XSD definitions
*
* @author Ole.Matzura
*/
public class SoapMessageBuilder implements MessageBuilder
{
private final static Logger log = Logger.getLogger( SoapMessageBuilder.class );
private WsdlContext wsdlContext;
private WsdlInterface iface;
private Map<QName, String[]> multiValues = null;
public SoapMessageBuilder( WsdlInterface iface ) throws Exception
{
this.iface = iface;
this.wsdlContext = iface.getWsdlContext();
}
public SoapMessageBuilder( WsdlContext wsdlContext )
{
this.wsdlContext = wsdlContext;
}
public void setMultiValues( Map<QName, String[]> multiValues )
{
this.multiValues = multiValues;
}
public Interface getInterface()
{
return iface;
}
public String buildFault( String faultcode, String faultstring )
{
return buildFault( faultcode, faultstring, getSoapVersion() );
}
public SoapVersion getSoapVersion()
{
return iface == null ? wsdlContext.getSoapVersion() : iface.getSoapVersion();
}
public static String buildFault( String faultcode, String faultstring, SoapVersion soapVersion )
{
SampleXmlUtil generator = new SampleXmlUtil( false );
generator.setTypeComment( false );
generator.setIgnoreOptional( true );
String emptyResponse = buildEmptyFault( generator, soapVersion );
if( soapVersion == SoapVersion.Soap11 )
{
emptyResponse = XmlUtils.setXPathContent( emptyResponse, "//faultcode", faultcode );
emptyResponse = XmlUtils.setXPathContent( emptyResponse, "//faultstring", faultstring );
}
else if( soapVersion == SoapVersion.Soap12 )
{
emptyResponse = XmlUtils.setXPathContent( emptyResponse, "//soap:Value", faultcode );
emptyResponse = XmlUtils.setXPathContent( emptyResponse, "//soap:Text", faultstring );
emptyResponse = XmlUtils.setXPathContent( emptyResponse, "//soap:Text/@xml:lang", "en" );
}
return emptyResponse;
}
public String buildEmptyFault()
{
return buildEmptyFault( getSoapVersion() );
}
public static String buildEmptyFault( SoapVersion soapVersion )
{
SampleXmlUtil generator = new SampleXmlUtil( false );
String emptyResponse = buildEmptyFault( generator, soapVersion );
return emptyResponse;
}
private static String buildEmptyFault( SampleXmlUtil generator, SoapVersion soapVersion )
{
String emptyResponse = buildEmptyMessage( soapVersion );
try
{
// XmlObject xmlObject = XmlObject.Factory.parse( emptyResponse );
XmlObject xmlObject = XmlUtils.createXmlObject( emptyResponse );
XmlCursor cursor = xmlObject.newCursor();
if( cursor.toChild( soapVersion.getEnvelopeQName() ) && cursor.toChild( soapVersion.getBodyQName() ) )
{
SchemaType faultType = soapVersion.getFaultType();
Node bodyNode = cursor.getDomNode();
Document dom = XmlUtils.parseXml( generator.createSample( faultType ) );
bodyNode.appendChild( bodyNode.getOwnerDocument().importNode( dom.getDocumentElement(), true ) );
}
cursor.dispose();
emptyResponse = xmlObject.toString();
}
catch( Exception e )
{
SoapUI.logError( e );
}
return emptyResponse;
}
public String buildEmptyMessage()
{
return buildEmptyMessage( getSoapVersion() );
}
public static String buildEmptyMessage( SoapVersion soapVersion )
{
SampleXmlUtil generator = new SampleXmlUtil( false );
generator.setTypeComment( false );
generator.setIgnoreOptional( true );
return generator.createSample( soapVersion.getEnvelopeType() );
}
public String buildSoapMessageFromInput( BindingOperation bindingOperation, boolean buildOptional ) throws Exception
{
return buildSoapMessageFromInput( bindingOperation, buildOptional, true );
}
public String buildSoapMessageFromInput( BindingOperation bindingOperation, boolean buildOptional,
boolean alwaysBuildHeaders ) throws Exception
{
boolean inputSoapEncoded = WsdlUtils.isInputSoapEncoded( bindingOperation );
SampleXmlUtil xmlGenerator = new SampleXmlUtil( inputSoapEncoded );
xmlGenerator.setMultiValues( multiValues );
xmlGenerator.setIgnoreOptional( !buildOptional );
XmlObject object = XmlObject.Factory.newInstance();
XmlCursor cursor = object.newCursor();
cursor.toNextToken();
cursor.beginElement( wsdlContext.getSoapVersion().getEnvelopeQName() );
if( inputSoapEncoded )
{
cursor.insertNamespace( "xsi", Constants.XSI_NS );
cursor.insertNamespace( "xsd", Constants.XSD_NS );
}
cursor.toFirstChild();
cursor.beginElement( wsdlContext.getSoapVersion().getBodyQName() );
cursor.toFirstChild();
if( WsdlUtils.isRpc( wsdlContext.getDefinition(), bindingOperation ) )
{
buildRpcRequest( bindingOperation, cursor, xmlGenerator );
}
else
{
buildDocumentRequest( bindingOperation, cursor, xmlGenerator );
}
if( alwaysBuildHeaders )
{
BindingInput bindingInput = bindingOperation.getBindingInput();
if( bindingInput != null )
{
List<?> extensibilityElements = bindingInput.getExtensibilityElements();
List<SoapHeader> soapHeaders = WsdlUtils.getSoapHeaders( extensibilityElements );
addHeaders( soapHeaders, cursor, xmlGenerator );
}
}
cursor.dispose();
try
{
StringWriter writer = new StringWriter();
XmlUtils.serializePretty( object, writer );
return writer.toString();
}
catch( Exception e )
{
SoapUI.logError( e );
return object.xmlText();
}
}
private void addHeaders( List<SoapHeader> headers, XmlCursor cursor, SampleXmlUtil xmlGenerator ) throws Exception
{
// reposition
cursor.toStartDoc();
cursor.toChild( wsdlContext.getSoapVersion().getEnvelopeQName() );
cursor.toFirstChild();
cursor.beginElement( wsdlContext.getSoapVersion().getHeaderQName() );
cursor.toFirstChild();
for( int i = 0; i < headers.size(); i++ )
{
SoapHeader header = headers.get( i );
Message message = wsdlContext.getDefinition().getMessage( header.getMessage() );
if( message == null )
{
log.error( "Missing message for header: " + header.getMessage() );
continue;
}
Part part = message.getPart( header.getPart() );
if( part != null )
createElementForPart( part, cursor, xmlGenerator );
else
log.error( "Missing part for header; " + header.getPart() );
}
}
public void createElementForPart( Part part, XmlCursor cursor, SampleXmlUtil xmlGenerator ) throws Exception
{
QName elementName = part.getElementName();
QName typeName = part.getTypeName();
if( elementName != null )
{
cursor.beginElement( elementName );
if( wsdlContext.hasSchemaTypes() )
{
SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement( elementName );
if( elm != null )
{
cursor.toFirstChild();
xmlGenerator.createSampleForType( elm.getType(), cursor );
}
else
log.error( "Could not find element [" + elementName + "] specified in part [" + part.getName() + "]" );
}
cursor.toParent();
}
else
{
// cursor.beginElement( new QName(
// wsdlContext.getWsdlDefinition().getTargetNamespace(), part.getName()
// ));
cursor.beginElement( part.getName() );
if( typeName != null && wsdlContext.hasSchemaTypes() )
{
SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
if( type != null )
{
cursor.toFirstChild();
xmlGenerator.createSampleForType( type, cursor );
}
else
log.error( "Could not find type [" + typeName + "] specified in part [" + part.getName() + "]" );
}
cursor.toParent();
}
}
private void buildDocumentRequest( BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator )
throws Exception
{
Part[] parts = WsdlUtils.getInputParts( bindingOperation );
for( int i = 0; i < parts.length; i++ )
{
Part part = parts[i];
if( !WsdlUtils.isAttachmentInputPart( part, bindingOperation )
&& ( part.getElementName() != null || part.getTypeName() != null ) )
{
XmlCursor c = cursor.newCursor();
c.toLastChild();
createElementForPart( part, c, xmlGenerator );
c.dispose();
}
}
}
private void buildDocumentResponse( BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator )
throws Exception
{
Part[] parts = WsdlUtils.getOutputParts( bindingOperation );
for( int i = 0; i < parts.length; i++ )
{
Part part = parts[i];
if( !WsdlUtils.isAttachmentOutputPart( part, bindingOperation )
&& ( part.getElementName() != null || part.getTypeName() != null ) )
{
XmlCursor c = cursor.newCursor();
c.toLastChild();
createElementForPart( part, c, xmlGenerator );
c.dispose();
}
}
}
private void buildRpcRequest( BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator )
throws Exception
{
// rpc requests use the operation name as root element
String ns = WsdlUtils.getSoapBodyNamespace( bindingOperation.getBindingInput().getExtensibilityElements() );
if( ns == null )
{
ns = WsdlUtils.getTargetNamespace( wsdlContext.getDefinition() );
log.warn( "missing namespace on soapbind:body for RPC request, using targetNamespace instead (BP violation)" );
}
cursor.beginElement( new QName( ns, bindingOperation.getName() ) );
if( xmlGenerator.isSoapEnc() )
cursor.insertAttributeWithValue( new QName( wsdlContext.getSoapVersion().getEnvelopeNamespace(),
"encodingStyle" ), wsdlContext.getSoapVersion().getEncodingNamespace() );
Part[] inputParts = WsdlUtils.getInputParts( bindingOperation );
for( int i = 0; i < inputParts.length; i++ )
{
Part part = inputParts[i];
if( WsdlUtils.isAttachmentInputPart( part, bindingOperation ) )
{
if( iface.getSettings().getBoolean( WsdlSettings.ATTACHMENT_PARTS ) )
{
XmlCursor c = cursor.newCursor();
c.toLastChild();
c.beginElement( part.getName() );
c.insertAttributeWithValue( "href", part.getName() + "Attachment" );
c.dispose();
}
}
else
{
if( wsdlContext.hasSchemaTypes() )
{
QName typeName = part.getTypeName();
if( typeName != null )
{
SchemaType type = wsdlContext.getInterfaceDefinition().findType( typeName );
if( type != null )
{
XmlCursor c = cursor.newCursor();
c.toLastChild();
c.insertElement( part.getName() );
c.toPrevToken();
xmlGenerator.createSampleForType( type, c );
c.dispose();
}
else
log.warn( "Failed to find type [" + typeName + "]" );
}
else
{
SchemaGlobalElement element = wsdlContext.getSchemaTypeLoader().findElement( part.getElementName() );
if( element != null )
{
XmlCursor c = cursor.newCursor();
c.toLastChild();
c.insertElement( element.getName() );
c.toPrevToken();
xmlGenerator.createSampleForType( element.getType(), c );
c.dispose();
}
else
log.warn( "Failed to find element [" + part.getElementName() + "]" );
}
}
}
}
}
private void buildRpcResponse( BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator )
throws Exception
{
// rpc requests use the operation name as root element
BindingOutput bindingOutput = bindingOperation.getBindingOutput();
String ns = bindingOutput == null ? null : WsdlUtils.getSoapBodyNamespace( bindingOutput
.getExtensibilityElements() );
if( ns == null )
{
ns = WsdlUtils.getTargetNamespace( wsdlContext.getDefinition() );
log.warn( "missing namespace on soapbind:body for RPC response, using targetNamespace instead (BP violation)" );
}
cursor.beginElement( new QName( ns, bindingOperation.getName() + "Response" ) );
if( xmlGenerator.isSoapEnc() )
cursor.insertAttributeWithValue( new QName( wsdlContext.getSoapVersion().getEnvelopeNamespace(),
"encodingStyle" ), wsdlContext.getSoapVersion().getEncodingNamespace() );
Part[] inputParts = WsdlUtils.getOutputParts( bindingOperation );
for( int i = 0; i < inputParts.length; i++ )
{
Part part = inputParts[i];
if( WsdlUtils.isAttachmentOutputPart( part, bindingOperation ) )
{
if( iface.getSettings().getBoolean( WsdlSettings.ATTACHMENT_PARTS ) )
{
XmlCursor c = cursor.newCursor();
c.toLastChild();
c.beginElement( part.getName() );
c.insertAttributeWithValue( "href", part.getName() + "Attachment" );
c.dispose();
}
}
else
{
if( wsdlContext.hasSchemaTypes() )
{
QName typeName = part.getTypeName();
if( typeName != null )
{
SchemaType type = wsdlContext.getInterfaceDefinition().findType( typeName );
if( type != null )
{
XmlCursor c = cursor.newCursor();
c.toLastChild();
c.insertElement( part.getName() );
c.toPrevToken();
xmlGenerator.createSampleForType( type, c );
c.dispose();
}
else
log.warn( "Failed to find type [" + typeName + "]" );
}
else
{
SchemaGlobalElement element = wsdlContext.getSchemaTypeLoader().findElement( part.getElementName() );
if( element != null )
{
XmlCursor c = cursor.newCursor();
c.toLastChild();
c.insertElement( element.getName() );
c.toPrevToken();
xmlGenerator.createSampleForType( element.getType(), c );
c.dispose();
}
else
log.warn( "Failed to find element [" + part.getElementName() + "]" );
}
}
}
}
}
public void setWsdlContext( WsdlContext wsdlContext )
{
this.wsdlContext = wsdlContext;
}
public void setInterface( WsdlInterface iface )
{
this.iface = iface;
}
public String buildSoapMessageFromOutput( BindingOperation bindingOperation, boolean buildOptional )
throws Exception
{
return buildSoapMessageFromOutput( bindingOperation, buildOptional, true );
}
public String buildSoapMessageFromOutput( BindingOperation bindingOperation, boolean buildOptional,
boolean alwaysBuildHeaders ) throws Exception
{
boolean inputSoapEncoded = WsdlUtils.isInputSoapEncoded( bindingOperation );
SampleXmlUtil xmlGenerator = new SampleXmlUtil( inputSoapEncoded );
xmlGenerator.setIgnoreOptional( !buildOptional );
xmlGenerator.setMultiValues( multiValues );
XmlObject object = XmlObject.Factory.newInstance();
XmlCursor cursor = object.newCursor();
cursor.toNextToken();
cursor.beginElement( wsdlContext.getSoapVersion().getEnvelopeQName() );
if( inputSoapEncoded )
{
cursor.insertNamespace( "xsi", Constants.XSI_NS );
cursor.insertNamespace( "xsd", Constants.XSD_NS );
}
cursor.toFirstChild();
cursor.beginElement( wsdlContext.getSoapVersion().getBodyQName() );
cursor.toFirstChild();
if( WsdlUtils.isRpc( wsdlContext.getDefinition(), bindingOperation ) )
{
buildRpcResponse( bindingOperation, cursor, xmlGenerator );
}
else
{
buildDocumentResponse( bindingOperation, cursor, xmlGenerator );
}
if( alwaysBuildHeaders )
{
// bindingOutput will be null for one way operations,
// but then we shouldn't be here in the first place???
BindingOutput bindingOutput = bindingOperation.getBindingOutput();
if( bindingOutput != null )
{
List<?> extensibilityElements = bindingOutput.getExtensibilityElements();
List<SoapHeader> soapHeaders = WsdlUtils.getSoapHeaders( extensibilityElements );
addHeaders( soapHeaders, cursor, xmlGenerator );
}
}
cursor.dispose();
try
{
StringWriter writer = new StringWriter();
XmlUtils.serializePretty( object, writer );
return writer.toString();
}
catch( Exception e )
{
SoapUI.logError( e );
return object.xmlText();
}
}
public String buildFault( FaultPart faultPart )
{
SampleXmlUtil generator = new SampleXmlUtil( false );
generator.setExampleContent( false );
generator.setTypeComment( false );
generator.setMultiValues( multiValues );
String faultResponse = iface.getMessageBuilder().buildEmptyFault();
XmlCursor cursor = null;
try
{
// XmlObject xmlObject = XmlObject.Factory.parse( faultResponse );
XmlObject xmlObject = XmlUtils.createXmlObject( faultResponse );
XmlObject[] detail = xmlObject.selectPath( "//detail" );
if( detail.length > 0 )
{
cursor = detail[0].newCursor();
cursor.toFirstContentToken();
generator.setTypeComment( true );
generator.setIgnoreOptional( iface.getSettings().getBoolean(
WsdlSettings.XML_GENERATION_ALWAYS_INCLUDE_OPTIONAL_ELEMENTS ) );
for( Part part : faultPart.getWsdlParts() )
createElementForPart( part, cursor, generator );
}
faultResponse = xmlObject.xmlText( new XmlOptions().setSaveAggressiveNamespaces().setSavePrettyPrint() );
}
catch( Exception e1 )
{
SoapUI.logError( e1 );
}
finally
{
if( cursor != null )
cursor.dispose();
}
return faultResponse;
}
}