View Javadoc

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