View Javadoc

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