View Javadoc

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