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 }