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.soap;
    14
    15  import java.io.StringWriter;
    16  import java.util.List;
    17  import java.util.Map;
    18
    19  import javax.wsdl.BindingOperation;
    20  import javax.wsdl.Message;
    21  import javax.wsdl.Part;
    22  import javax.xml.namespace.QName;
    23
    24  import org.apache.log4j.Logger;
    25  import org.apache.xmlbeans.SchemaGlobalElement;
    26  import org.apache.xmlbeans.SchemaType;
    27  import org.apache.xmlbeans.XmlCursor;
    28  import org.apache.xmlbeans.XmlObject;
    29  import org.apache.xmlbeans.XmlOptions;
    30  import org.w3c.dom.Document;
    31  import org.w3c.dom.Node;
    32
    33  import com.eviware.soapui.SoapUI;
    34  import com.eviware.soapui.impl.wsdl.WsdlInterface;
    35  import com.eviware.soapui.impl.wsdl.support.Constants;
    36  import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlContext;
    37  import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils;
    38  import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils.SoapHeader;
    39  import com.eviware.soapui.impl.wsdl.support.xsd.SampleXmlUtil;
    40  import com.eviware.soapui.model.iface.Interface;
    41  import com.eviware.soapui.model.iface.MessageBuilder;
    42  import com.eviware.soapui.model.iface.Operation;
    43  import com.eviware.soapui.model.iface.Request;
    44  import com.eviware.soapui.model.iface.MessagePart.FaultPart;
    45  import com.eviware.soapui.settings.WsdlSettings;
    46  import com.eviware.soapui.support.xml.XmlUtils;
    47
    48  /***
    49   * Builds SOAP requests according to WSDL/XSD definitions
    50   *
    51   * @author Ole.Matzura
    52   */
    53
    54  public class SoapMessageBuilder implements MessageBuilder
    55  {
    56     private final static Logger log = Logger.getLogger( SoapMessageBuilder.class );
    57
    58     private WsdlContext wsdlContext;
    59     private WsdlInterface iface;
    60
    61     public SoapMessageBuilder(WsdlInterface iface) throws Exception
    62     {
    63        this.iface = iface;
    64        this.wsdlContext = iface.getWsdlContext();
    65     }
    66
    67     public SoapMessageBuilder(WsdlContext wsdlContext)
    68     {
    69        this.wsdlContext = wsdlContext;
    70     }
    71
    72     public Interface getInterface()
    73     {
    74        return iface;
    75     }
    76
    77     public String buildFault( String faultcode, String faultstring )
    78     {
    79     	return buildFault( faultcode, faultstring, getSoapVersion() );
    80     }
    81
    82  	public SoapVersion getSoapVersion()
    83  	{
    84  		return iface == null ? wsdlContext.getSoapVersion() : iface.getSoapVersion();
    85  	}
    86
    87     public static String buildFault( String faultcode, String faultstring, SoapVersion soapVersion )
    88     {
    89     	SampleXmlUtil generator = new SampleXmlUtil( false );
    90     	generator.setTypeComment( false );
    91     	generator.setIgnoreOptional( true );
    92
    93     	String emptyResponse = buildEmptyFault( generator, soapVersion );
    94
    95     	if( soapVersion == SoapVersion.Soap11 )
    96     	{
    97     		emptyResponse = XmlUtils.setXPathContent( emptyResponse, "//faultcode", faultcode );
    98     		emptyResponse = XmlUtils.setXPathContent( emptyResponse, "//faultstring", faultstring );
    99     	}
    100    	else if( soapVersion == SoapVersion.Soap12 )
    101    	{
    102    		emptyResponse = XmlUtils.setXPathContent( emptyResponse, "//soap:Value", faultcode );
    103    		emptyResponse = XmlUtils.setXPathContent( emptyResponse, "//soap:Text", faultstring );
    104    		emptyResponse = XmlUtils.setXPathContent( emptyResponse, "//soap:Text/@xml:lang", "en" );
    105    	}
    106
    107 		return emptyResponse;
    108 	}
    109
    110    public String buildEmptyFault()
    111    {
    112    	return buildEmptyFault( getSoapVersion() );
    113    }
    114
    115    public static String buildEmptyFault( SoapVersion soapVersion )
    116 	{
    117    	SampleXmlUtil generator = new SampleXmlUtil( false );
    118
    119    	String emptyResponse = buildEmptyFault( generator, soapVersion );
    120
    121 		return emptyResponse;
    122 	}
    123
    124 	private static String buildEmptyFault( SampleXmlUtil generator, SoapVersion soapVersion )
    125 	{
    126 		String emptyResponse = buildEmptyMessage( soapVersion );
    127 		try
    128 		{
    129 			XmlObject xmlObject = XmlObject.Factory.parse( emptyResponse );
    130 			XmlCursor cursor = xmlObject.newCursor();
    131
    132 			if( cursor.toChild( soapVersion.getEnvelopeQName() ) && cursor.toChild( soapVersion.getBodyQName() ))
    133 			{
    134 				SchemaType faultType = soapVersion.getFaultType();
    135 				Node bodyNode = cursor.getDomNode();
    136 				Document dom = XmlUtils.parseXml( generator.createSample( faultType ) );
    137 				bodyNode.appendChild( bodyNode.getOwnerDocument().importNode( dom.getDocumentElement(), true ) );
    138 			}
    139
    140 			cursor.dispose();
    141 			emptyResponse = xmlObject.toString();
    142 		}
    143 		catch( Exception e )
    144 		{
    145 			SoapUI.logError( e );
    146 		}
    147 		return emptyResponse;
    148 	}
    149
    150 	public String buildEmptyMessage()
    151 	{
    152 		return buildEmptyMessage( getSoapVersion() );
    153 	}
    154
    155    public static String buildEmptyMessage( SoapVersion soapVersion )
    156 	{
    157    	SampleXmlUtil generator = new SampleXmlUtil( false );
    158    	generator.setTypeComment( false );
    159    	generator.setIgnoreOptional( true );
    160    	return generator.createSample( soapVersion.getEnvelopeType() );
    161 	}
    162
    163 	public Request buildRequest(Operation operation, Map params)
    164    {
    165       return null;
    166    }
    167
    168    public String buildSoapRequest(BindingOperation bindingOperation, boolean buildOptional ) throws Exception
    169    {
    170       boolean inputSoapEncoded = WsdlUtils.isInputSoapEncoded( bindingOperation );
    171       SampleXmlUtil xmlGenerator = new SampleXmlUtil(inputSoapEncoded);
    172       xmlGenerator.setIgnoreOptional( !buildOptional );
    173
    174       XmlObject object = XmlObject.Factory.newInstance();
    175       XmlCursor cursor = object.newCursor();
    176       cursor.toNextToken();
    177       cursor.beginElement( wsdlContext.getSoapVersion().getEnvelopeQName() );
    178
    179       if( inputSoapEncoded )
    180       {
    181          cursor.insertNamespace( "xsi", Constants.XSI_NS );
    182          cursor.insertNamespace( "xsd", Constants.XSD_NS );
    183       }
    184
    185       cursor.toFirstChild();
    186
    187       cursor.beginElement(wsdlContext.getSoapVersion().getBodyQName());
    188       cursor.toFirstChild();
    189
    190       if( WsdlUtils.isRpc( wsdlContext.getDefinition(), bindingOperation ))
    191       {
    192          buildRpcRequest( bindingOperation, cursor, xmlGenerator );
    193       }
    194       else
    195       {
    196          buildDocumentRequest( bindingOperation, cursor, xmlGenerator );
    197       }
    198
    199       addHeaders( WsdlUtils.getSoapHeaders( bindingOperation.getBindingInput().getExtensibilityElements()),
    200       			cursor, xmlGenerator);
    201       cursor.dispose();
    202
    203       try
    204       {
    205          StringWriter writer = new StringWriter();
    206          XmlUtils.serializePretty( object, writer );
    207          return writer.toString();
    208       }
    209       catch( Exception e )
    210       {
    211          SoapUI.logError( e );
    212          return object.xmlText();
    213       }
    214    }
    215
    216 	private void addHeaders(List<SoapHeader> headers, XmlCursor cursor, SampleXmlUtil xmlGenerator) throws Exception
    217    {
    218       // reposition
    219       cursor.toStartDoc();
    220       cursor.toChild(wsdlContext.getSoapVersion().getEnvelopeQName());
    221       cursor.toFirstChild();
    222
    223       cursor.beginElement(wsdlContext.getSoapVersion().getHeaderQName());
    224       cursor.toFirstChild();
    225
    226       for (int i = 0; i < headers.size(); i++)
    227       {
    228          SoapHeader header = (SoapHeader) headers.get(i);
    229
    230          Message message = wsdlContext.getDefinition().getMessage( header.getMessage() );
    231          if( message == null )
    232          {
    233          	log.error( "Missing message for header: " + header.getMessage() );
    234          	continue;
    235          }
    236
    237 			Part part = message.getPart( header.getPart() );
    238
    239          if( part != null )
    240          	createElementForPart( part, cursor, xmlGenerator );
    241          else
    242          	log.error( "Missing part for header; " + header.getPart() );
    243       }
    244    }
    245
    246    public void createElementForPart(Part part, XmlCursor cursor, SampleXmlUtil xmlGenerator ) throws Exception
    247    {
    248       QName elementName = part.getElementName();
    249       QName typeName = part.getTypeName();
    250
    251       if( elementName != null )
    252       {
    253          cursor.beginElement(elementName);
    254
    255          if( wsdlContext.hasSchemaTypes() )
    256          {
    257             SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement(elementName);
    258             if( elm != null )
    259             {
    260                cursor.toFirstChild();
    261                xmlGenerator.createSampleForType(elm.getType(), cursor);
    262             }
    263             else log.error( "Could not find element [" + elementName + "] specified in part [" + part.getName() + "]" );
    264          }
    265
    266          cursor.toParent();
    267       }
    268       else
    269       {
    270 //         cursor.beginElement( new QName( wsdlContext.getDefinition().getTargetNamespace(), part.getName() ));
    271          cursor.beginElement( part.getName() );
    272          if( typeName != null && wsdlContext.hasSchemaTypes() )
    273          {
    274             SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
    275
    276             if( type != null )
    277             {
    278                cursor.toFirstChild();
    279                xmlGenerator.createSampleForType( type, cursor );
    280             }
    281             else log.error( "Could not find type [" + typeName + "] specified in part [" + part.getName() + "]" );
    282          }
    283
    284          cursor.toParent();
    285       }
    286    }
    287
    288    private void buildDocumentRequest(BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator) throws Exception
    289    {
    290       Part[] parts = WsdlUtils.getInputParts( bindingOperation );
    291
    292       for( int i = 0; i < parts.length; i++ )
    293       {
    294       	if( !WsdlUtils.isAttachmentInputPart( parts[i], bindingOperation ))
    295       	{
    296 		      XmlCursor c = cursor.newCursor();
    297 		      c.toLastChild();
    298 		      createElementForPart( parts[i], c, xmlGenerator );
    299 		      c.dispose();
    300       	}
    301       }
    302    }
    303
    304    private void buildDocumentResponse(BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator) throws Exception
    305    {
    306       Part[] parts = WsdlUtils.getOutputParts( bindingOperation );
    307
    308       for( int i = 0; i < parts.length; i++ )
    309       {
    310       	if( !WsdlUtils.isAttachmentOutputPart( parts[i], bindingOperation ))
    311       	{
    312 		      XmlCursor c = cursor.newCursor();
    313 		      c.toLastChild();
    314 		      createElementForPart( parts[i], c, xmlGenerator );
    315 		      c.dispose();
    316       	}
    317       }
    318    }
    319
    320    private void buildRpcRequest(BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator) throws Exception
    321    {
    322       // rpc requests use the operation name as root element
    323       String ns = WsdlUtils.getSoapBodyNamespace( bindingOperation.getBindingInput()
    324                .getExtensibilityElements() );
    325       if( ns == null )
    326       {
    327       	ns = wsdlContext.getDefinition().getTargetNamespace();
    328       	log.warn( "missing namespace on soapbind:body, using targetNamespace instead (BP violation)" );
    329       }
    330
    331       cursor.beginElement( new QName( ns, bindingOperation.getName() ));
    332       if( xmlGenerator.isSoapEnc()  )
    333          cursor.insertAttributeWithValue( new QName( wsdlContext.getSoapVersion().getEnvelopeNamespace(), "encodingStyle" ),
    334          		wsdlContext.getSoapVersion().getEncodingNamespace() );
    335
    336       Part[] inputParts = WsdlUtils.getInputParts( bindingOperation );
    337       for (int i = 0; i < inputParts.length; i++)
    338       {
    339          Part part = inputParts[i];
    340          if( WsdlUtils.isAttachmentInputPart( part, bindingOperation ))
    341          {
    342          	if( iface.getSettings().getBoolean( WsdlSettings.ATTACHMENT_PARTS ))
    343          	{
    344          		XmlCursor c = cursor.newCursor();
    345    	         c.toLastChild();
    346    	         c.beginElement( part.getName() );
    347    	         c.insertAttributeWithValue( "href", part.getName() + "Attachment" );
    348    	         c.dispose();
    349          	}
    350          }
    351          else
    352          {
    353 	         if( wsdlContext.hasSchemaTypes() )
    354 	         {
    355 	            QName typeName = part.getTypeName();
    356 	            if( typeName != null )
    357 	            {
    358 						SchemaType type = wsdlContext.findType( typeName );
    359
    360 		            if( type != null )
    361 		            {
    362 		            	XmlCursor c = cursor.newCursor();
    363 		   	         c.toLastChild();
    364 		   	         c.insertElement( part.getName() );
    365 		   	         c.toPrevToken();
    366
    367 		               xmlGenerator.createSampleForType( type, c );
    368 		               c.dispose();
    369 		            }
    370 		            else
    371 		               log.warn( "Failed to find type [" + typeName + "]" );
    372 	            }
    373 	            else
    374 	            {
    375 	            	SchemaGlobalElement element = wsdlContext.getSchemaTypeLoader().findElement( part.getElementName() );
    376 	            	if( element != null )
    377 	            	{
    378 	            		XmlCursor c = cursor.newCursor();
    379 	      	         c.toLastChild();
    380 	      	         c.insertElement( element.getName() );
    381 	      	         c.toPrevToken();
    382
    383 	            		xmlGenerator.createSampleForType( element.getType(), c );
    384 	            		c.dispose();
    385 	            	}
    386 		            else
    387 		               log.warn( "Failed to find element [" + part.getElementName() + "]" );
    388 	            }
    389 	         }
    390          }
    391       }
    392    }
    393
    394    private void buildRpcResponse(BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator) throws Exception
    395    {
    396       // rpc requests use the operation name as root element
    397    	String ns = WsdlUtils.getSoapBodyNamespace( bindingOperation.getBindingOutput()
    398                 .getExtensibilityElements() );
    399
    400    	if( ns == null )
    401       {
    402        	ns = wsdlContext.getDefinition().getTargetNamespace();
    403        	log.warn( "missing namespace on soapbind:body, using targetNamespace instead (BP violation)" );
    404       }
    405
    406       cursor.beginElement( new QName( ns, bindingOperation.getName() + "Response" ));
    407       if( xmlGenerator.isSoapEnc()  )
    408          cursor.insertAttributeWithValue( new QName( wsdlContext.getSoapVersion().getEnvelopeNamespace(), "encodingStyle" ),
    409          		wsdlContext.getSoapVersion().getEncodingNamespace() );
    410
    411       Part[] inputParts = WsdlUtils.getOutputParts( bindingOperation );
    412       for (int i = 0; i < inputParts.length; i++)
    413       {
    414          Part part = inputParts[i];
    415          if( WsdlUtils.isAttachmentOutputPart( part, bindingOperation ))
    416          {
    417          	if( iface.getSettings().getBoolean( WsdlSettings.ATTACHMENT_PARTS ))
    418          	{
    419          		XmlCursor c = cursor.newCursor();
    420    	         c.toLastChild();
    421    	         c.beginElement( part.getName() );
    422    	         c.insertAttributeWithValue( "href", part.getName() + "Attachment" );
    423    	         c.dispose();
    424          	}
    425          }
    426          else
    427          {
    428 	         if( wsdlContext.hasSchemaTypes() )
    429 	         {
    430 	            QName typeName = part.getTypeName();
    431 	            if( typeName != null )
    432 	            {
    433 						SchemaType type = wsdlContext.findType( typeName );
    434
    435 		            if( type != null )
    436 		            {
    437 		            	XmlCursor c = cursor.newCursor();
    438 		   	         c.toLastChild();
    439 		   	         c.insertElement( part.getName() );
    440 		   	         c.toPrevToken();
    441
    442 		               xmlGenerator.createSampleForType( type, c );
    443 		               c.dispose();
    444 		            }
    445 		            else
    446 		               log.warn( "Failed to find type [" + typeName + "]" );
    447 	            }
    448 	            else
    449 	            {
    450 	            	SchemaGlobalElement element = wsdlContext.getSchemaTypeLoader().findElement( part.getElementName() );
    451 	            	if( element != null )
    452 	            	{
    453 	            		XmlCursor c = cursor.newCursor();
    454 	      	         c.toLastChild();
    455 	      	         c.insertElement( element.getName() );
    456 	      	         c.toPrevToken();
    457
    458 	            		xmlGenerator.createSampleForType( element.getType(), c );
    459 	            		c.dispose();
    460 	            	}
    461 		            else
    462 		               log.warn( "Failed to find element [" + part.getElementName() + "]" );
    463 	            }
    464 	         }
    465          }
    466       }
    467    }
    468
    469    public void setWsdlContext( WsdlContext wsdlContext )
    470    {
    471    	this.wsdlContext = wsdlContext;
    472    }
    473
    474 	public void setInterface(WsdlInterface iface )
    475 	{
    476 		this.iface = iface;
    477 	}
    478
    479 	public String buildSoapResponse( BindingOperation bindingOperation, boolean buildOptional ) throws Exception
    480 	{
    481 		boolean inputSoapEncoded = WsdlUtils.isInputSoapEncoded( bindingOperation );
    482       SampleXmlUtil xmlGenerator = new SampleXmlUtil(inputSoapEncoded);
    483       xmlGenerator.setIgnoreOptional( !buildOptional );
    484
    485       XmlObject object = XmlObject.Factory.newInstance();
    486       XmlCursor cursor = object.newCursor();
    487       cursor.toNextToken();
    488       cursor.beginElement( wsdlContext.getSoapVersion().getEnvelopeQName() );
    489
    490       if( inputSoapEncoded )
    491       {
    492          cursor.insertNamespace( "xsi", Constants.XSI_NS );
    493          cursor.insertNamespace( "xsd", Constants.XSD_NS );
    494       }
    495
    496       cursor.toFirstChild();
    497
    498       cursor.beginElement(wsdlContext.getSoapVersion().getBodyQName());
    499       cursor.toFirstChild();
    500
    501       if( WsdlUtils.isRpc( wsdlContext.getDefinition(), bindingOperation ))
    502       {
    503          buildRpcResponse( bindingOperation, cursor, xmlGenerator );
    504       }
    505       else
    506       {
    507          buildDocumentResponse( bindingOperation, cursor, xmlGenerator );
    508       }
    509
    510       addHeaders(WsdlUtils.getSoapHeaders( bindingOperation.getBindingOutput()
    511                .getExtensibilityElements() ), cursor, xmlGenerator);
    512       cursor.dispose();
    513
    514       try
    515       {
    516          StringWriter writer = new StringWriter();
    517          XmlUtils.serializePretty( object, writer );
    518          return writer.toString();
    519       }
    520       catch( Exception e )
    521       {
    522          SoapUI.logError( e );
    523          return object.xmlText();
    524       }
    525 	}
    526
    527
    528 	public String buildFault( FaultPart faultPart )
    529 	{
    530 		SampleXmlUtil generator = new SampleXmlUtil( false );
    531 		generator.setExampleContent( false );
    532 		generator.setTypeComment( false );
    533 		String faultResponse = iface.getMessageBuilder().buildEmptyFault();
    534
    535 		XmlCursor cursor = null;
    536 		try
    537 		{
    538 			XmlObject xmlObject = XmlObject.Factory.parse( faultResponse );
    539 			XmlObject[] detail = xmlObject.selectPath( "//detail" );
    540 			cursor  = detail[0].newCursor();
    541
    542 			cursor.toFirstContentToken();
    543
    544 			generator.setTypeComment( true );
    545 			generator.setIgnoreOptional( iface.getSettings().getBoolean( WsdlSettings.XML_GENERATION_ALWAYS_INCLUDE_OPTIONAL_ELEMENTS ) );
    546
    547 			for( Part part : faultPart.getWsdlParts() )
    548 				createElementForPart( part, cursor, generator );
    549
    550 			faultResponse = xmlObject.xmlText( new XmlOptions().setSaveAggressiveNamespaces().setSavePrettyPrint());
    551 		}
    552 		catch( Exception e1 )
    553 		{
    554 			SoapUI.logError( e1 );
    555 		}
    556 		finally
    557 		{
    558 			if( cursor != null )
    559 				cursor.dispose();
    560 		}
    561
    562 		return faultResponse;
    563 	}
    564 }