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       return buildSoapRequest(bindingOperation, buildOptional, true);
171    }
172 
173    public String buildSoapRequest(BindingOperation bindingOperation, boolean buildOptional,
174          boolean alwaysBuildHeaders) throws Exception
175    {
176       boolean inputSoapEncoded = WsdlUtils.isInputSoapEncoded( bindingOperation );
177       SampleXmlUtil xmlGenerator = new SampleXmlUtil(inputSoapEncoded);
178       xmlGenerator.setIgnoreOptional( !buildOptional );
179       
180       XmlObject object = XmlObject.Factory.newInstance();
181       XmlCursor cursor = object.newCursor();
182       cursor.toNextToken();
183       cursor.beginElement( wsdlContext.getSoapVersion().getEnvelopeQName() );
184 
185       if( inputSoapEncoded )
186       {
187          cursor.insertNamespace( "xsi", Constants.XSI_NS );
188          cursor.insertNamespace( "xsd", Constants.XSD_NS );
189       }
190       
191       cursor.toFirstChild();
192 
193       cursor.beginElement(wsdlContext.getSoapVersion().getBodyQName());
194       cursor.toFirstChild();
195      
196       if( WsdlUtils.isRpc( wsdlContext.getDefinition(), bindingOperation ))
197       {
198          buildRpcRequest( bindingOperation, cursor, xmlGenerator );
199       }
200       else 
201       {
202          buildDocumentRequest( bindingOperation, cursor, xmlGenerator );
203       }   
204       
205       if(alwaysBuildHeaders)
206       {
207          List extensibilityElements = bindingOperation.getBindingInput().getExtensibilityElements();
208          List<SoapHeader> soapHeaders = WsdlUtils.getSoapHeaders( extensibilityElements);
209          addHeaders( soapHeaders, cursor, xmlGenerator);
210       }
211       cursor.dispose();
212 
213       try
214       {
215          StringWriter writer = new StringWriter();
216          XmlUtils.serializePretty( object, writer );
217          return writer.toString();
218       }
219       catch( Exception e )
220       {
221          SoapUI.logError( e );
222          return object.xmlText();
223       }
224    }
225 
226 	private void addHeaders(List<SoapHeader> headers, XmlCursor cursor, SampleXmlUtil xmlGenerator) throws Exception
227    {
228       // reposition
229       cursor.toStartDoc();
230       cursor.toChild(wsdlContext.getSoapVersion().getEnvelopeQName());
231       cursor.toFirstChild();
232       
233       cursor.beginElement(wsdlContext.getSoapVersion().getHeaderQName());
234       cursor.toFirstChild();
235 
236       for (int i = 0; i < headers.size(); i++)
237       {
238          SoapHeader header = headers.get(i);
239          
240          Message message = wsdlContext.getDefinition().getMessage( header.getMessage() );
241          if( message == null )
242          {
243          	log.error( "Missing message for header: " + header.getMessage() );
244          	continue;
245          }
246          
247 			Part part = message.getPart( header.getPart() );
248 
249          if( part != null )
250          	createElementForPart( part, cursor, xmlGenerator );
251          else
252          	log.error( "Missing part for header; " + header.getPart() );
253       }
254    }
255 
256    public void createElementForPart(Part part, XmlCursor cursor, SampleXmlUtil xmlGenerator ) throws Exception
257    {
258       QName elementName = part.getElementName();
259       QName typeName = part.getTypeName();
260       
261       if( elementName != null )
262       {
263          cursor.beginElement(elementName);
264    
265          if( wsdlContext.hasSchemaTypes() )
266          {
267             SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement(elementName);
268             if( elm != null )
269             {
270                cursor.toFirstChild();
271                xmlGenerator.createSampleForType(elm.getType(), cursor);
272             }
273             else log.error( "Could not find element [" + elementName + "] specified in part [" + part.getName() + "]" );
274          }
275 
276          cursor.toParent();
277       }
278       else 
279       {
280 //         cursor.beginElement( new QName( wsdlContext.getDefinition().getTargetNamespace(), part.getName() ));
281          cursor.beginElement( part.getName() );
282          if( typeName != null && wsdlContext.hasSchemaTypes() )
283          {
284             SchemaType type = wsdlContext.getSchemaTypeLoader().findType( typeName );
285             
286             if( type != null )
287             {
288                cursor.toFirstChild();
289                xmlGenerator.createSampleForType( type, cursor );
290             }
291             else log.error( "Could not find type [" + typeName + "] specified in part [" + part.getName() + "]" );
292          }
293 
294          cursor.toParent();
295       }
296    }
297 
298    private void buildDocumentRequest(BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator) throws Exception
299    {
300       Part[] parts = WsdlUtils.getInputParts( bindingOperation );
301       
302       for( int i = 0; i < parts.length; i++ )
303       {
304       	Part part = parts[i];
305 			if( !WsdlUtils.isAttachmentInputPart( part, bindingOperation ) && 
306 				  (part.getElementName() != null || part.getTypeName() != null ))
307       	{
308 		      XmlCursor c = cursor.newCursor();
309 		      c.toLastChild();
310 		      createElementForPart( part, c, xmlGenerator );
311 		      c.dispose();
312       	}
313       }
314    }
315 
316    private void buildDocumentResponse(BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator) throws Exception
317    {
318       Part[] parts = WsdlUtils.getOutputParts( bindingOperation );
319       
320       for( int i = 0; i < parts.length; i++ )
321       {
322       	Part part = parts[i];
323       	
324 			if( !WsdlUtils.isAttachmentOutputPart( part, bindingOperation ) && 
325 			  (part.getElementName() != null || part.getTypeName() != null ))
326       	{
327 		      XmlCursor c = cursor.newCursor();
328 		      c.toLastChild();
329 		      createElementForPart( part, c, xmlGenerator );
330 		      c.dispose();
331       	}
332       }
333    }
334    
335    private void buildRpcRequest(BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator) throws Exception
336    {
337       // rpc requests use the operation name as root element
338       String ns = WsdlUtils.getSoapBodyNamespace( bindingOperation.getBindingInput()
339                .getExtensibilityElements() );
340       if( ns == null )
341       {
342       	ns = wsdlContext.getDefinition().getTargetNamespace();
343       	log.warn( "missing namespace on soapbind:body for RPC request, using targetNamespace instead (BP violation)" );
344       }
345       
346       cursor.beginElement( new QName( ns, bindingOperation.getName() ));
347       if( xmlGenerator.isSoapEnc()  )
348          cursor.insertAttributeWithValue( new QName( wsdlContext.getSoapVersion().getEnvelopeNamespace(), "encodingStyle" ), 
349          		wsdlContext.getSoapVersion().getEncodingNamespace() );
350       
351       Part[] inputParts = WsdlUtils.getInputParts( bindingOperation );
352       for (int i = 0; i < inputParts.length; i++)
353       {
354          Part part = inputParts[i];
355          if( WsdlUtils.isAttachmentInputPart( part, bindingOperation ))
356          {
357          	if( iface.getSettings().getBoolean( WsdlSettings.ATTACHMENT_PARTS ))
358          	{
359          		XmlCursor c = cursor.newCursor();
360    	         c.toLastChild();
361    	         c.beginElement( part.getName() );
362    	         c.insertAttributeWithValue( "href", part.getName() + "Attachment" );
363    	         c.dispose();
364          	}
365          }
366          else
367          {
368 	         if( wsdlContext.hasSchemaTypes() )
369 	         {
370 	            QName typeName = part.getTypeName();
371 	            if( typeName != null )
372 	            {
373 						SchemaType type = wsdlContext.findType( typeName );
374 		            
375 		            if( type != null )
376 		            {
377 		            	XmlCursor c = cursor.newCursor();
378 		   	         c.toLastChild();
379 		   	         c.insertElement( part.getName() );
380 		   	         c.toPrevToken();
381 		   	         
382 		               xmlGenerator.createSampleForType( type, c );
383 		               c.dispose();
384 		            }
385 		            else
386 		               log.warn( "Failed to find type [" + typeName + "]" );
387 	            }
388 	            else
389 	            {
390 	            	SchemaGlobalElement element = wsdlContext.getSchemaTypeLoader().findElement( part.getElementName() );
391 	            	if( element != null )
392 	            	{
393 	            		XmlCursor c = cursor.newCursor();
394 	      	         c.toLastChild();
395 	      	         c.insertElement( element.getName() );
396 	      	         c.toPrevToken();
397 	      	         
398 	            		xmlGenerator.createSampleForType( element.getType(), c );
399 	            		c.dispose();
400 	            	}
401 		            else
402 		               log.warn( "Failed to find element [" + part.getElementName() + "]" );
403 	            }
404 	         }
405          }
406       }
407    }
408    
409    private void buildRpcResponse(BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator) throws Exception
410    {
411       // rpc requests use the operation name as root element
412    	String ns = WsdlUtils.getSoapBodyNamespace( bindingOperation.getBindingOutput()
413                 .getExtensibilityElements() );
414        
415    	if( ns == null )
416       {
417        	ns = wsdlContext.getDefinition().getTargetNamespace();
418        	log.warn( "missing namespace on soapbind:body for RPC response, using targetNamespace instead (BP violation)" );
419       }
420       
421       cursor.beginElement( new QName( ns, bindingOperation.getName() + "Response" ));
422       if( xmlGenerator.isSoapEnc()  )
423          cursor.insertAttributeWithValue( new QName( wsdlContext.getSoapVersion().getEnvelopeNamespace(), "encodingStyle" ), 
424          		wsdlContext.getSoapVersion().getEncodingNamespace() );
425       
426       Part[] inputParts = WsdlUtils.getOutputParts( bindingOperation );
427       for (int i = 0; i < inputParts.length; i++)
428       {
429          Part part = inputParts[i];
430          if( WsdlUtils.isAttachmentOutputPart( part, bindingOperation ))
431          {
432          	if( iface.getSettings().getBoolean( WsdlSettings.ATTACHMENT_PARTS ))
433          	{
434          		XmlCursor c = cursor.newCursor();
435    	         c.toLastChild();
436    	         c.beginElement( part.getName() );
437    	         c.insertAttributeWithValue( "href", part.getName() + "Attachment" );
438    	         c.dispose();
439          	}
440          }
441          else
442          {
443 	         if( wsdlContext.hasSchemaTypes() )
444 	         {
445 	            QName typeName = part.getTypeName();
446 	            if( typeName != null )
447 	            {
448 						SchemaType type = wsdlContext.findType( typeName );
449 		            
450 		            if( type != null )
451 		            {
452 		            	XmlCursor c = cursor.newCursor();
453 		   	         c.toLastChild();
454 		   	         c.insertElement( part.getName() );
455 		   	         c.toPrevToken();
456 		   	         
457 		               xmlGenerator.createSampleForType( type, c );
458 		               c.dispose();
459 		            }
460 		            else
461 		               log.warn( "Failed to find type [" + typeName + "]" );
462 	            }
463 	            else
464 	            {
465 	            	SchemaGlobalElement element = wsdlContext.getSchemaTypeLoader().findElement( part.getElementName() );
466 	            	if( element != null )
467 	            	{
468 	            		XmlCursor c = cursor.newCursor();
469 	      	         c.toLastChild();
470 	      	         c.insertElement( element.getName() );
471 	      	         c.toPrevToken();
472 	      	         
473 	            		xmlGenerator.createSampleForType( element.getType(), c );
474 	            		c.dispose();
475 	            	}
476 		            else
477 		               log.warn( "Failed to find element [" + part.getElementName() + "]" );
478 	            }
479 	         }
480          }
481       }
482    }
483    
484    public void setWsdlContext( WsdlContext wsdlContext )
485    {
486    	this.wsdlContext = wsdlContext;
487    }
488 
489 	public void setInterface(WsdlInterface iface )
490 	{
491 		this.iface = iface;
492 	}
493 
494 	public String buildSoapResponse( BindingOperation bindingOperation, boolean buildOptional ) throws Exception
495 	{
496       return buildSoapResponse( bindingOperation, buildOptional, true );
497    }
498 
499    public String buildSoapResponse( BindingOperation bindingOperation, boolean buildOptional,
500          boolean alwaysBuildHeaders )
501    throws Exception
502    {
503 		boolean inputSoapEncoded = WsdlUtils.isInputSoapEncoded( bindingOperation );
504       SampleXmlUtil xmlGenerator = new SampleXmlUtil(inputSoapEncoded);
505       xmlGenerator.setIgnoreOptional( !buildOptional );
506       
507       XmlObject object = XmlObject.Factory.newInstance();
508       XmlCursor cursor = object.newCursor();
509       cursor.toNextToken();
510       cursor.beginElement( wsdlContext.getSoapVersion().getEnvelopeQName() );
511 
512       if( inputSoapEncoded )
513       {
514          cursor.insertNamespace( "xsi", Constants.XSI_NS );
515          cursor.insertNamespace( "xsd", Constants.XSD_NS );
516       }
517       
518       cursor.toFirstChild();
519 
520       cursor.beginElement(wsdlContext.getSoapVersion().getBodyQName());
521       cursor.toFirstChild();
522      
523       if( WsdlUtils.isRpc( wsdlContext.getDefinition(), bindingOperation ))
524       {
525          buildRpcResponse( bindingOperation, cursor, xmlGenerator );
526       }
527       else 
528       {
529          buildDocumentResponse( bindingOperation, cursor, xmlGenerator );
530       }   
531       
532       if(alwaysBuildHeaders)
533       {
534          List extensibilityElements = bindingOperation.getBindingOutput().getExtensibilityElements();
535          List<SoapHeader> soapHeaders = WsdlUtils.getSoapHeaders( extensibilityElements );
536          addHeaders(soapHeaders, cursor, xmlGenerator);
537       }
538       cursor.dispose();
539 
540       try
541       {
542          StringWriter writer = new StringWriter();
543          XmlUtils.serializePretty( object, writer );
544          return writer.toString();
545       }
546       catch( Exception e )
547       {
548          SoapUI.logError( e );
549          return object.xmlText();
550       }
551 	}
552 	
553 
554 	public String buildFault( FaultPart faultPart )
555 	{
556 		SampleXmlUtil generator = new SampleXmlUtil( false );
557 		generator.setExampleContent( false );
558 		generator.setTypeComment( false );
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 }