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.mock;
14  
15  import groovy.lang.Binding;
16  import groovy.lang.Script;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.util.ArrayList;
21  import java.util.List;
22  
23  import javax.activation.DataHandler;
24  import javax.mail.MessagingException;
25  import javax.mail.internet.MimeBodyPart;
26  import javax.mail.internet.MimeMessage;
27  import javax.mail.internet.MimeMultipart;
28  import javax.mail.internet.PreencodedMimeBodyPart;
29  import javax.swing.ImageIcon;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  
34  import com.eviware.soapui.SoapUI;
35  import com.eviware.soapui.config.AttachmentConfig;
36  import com.eviware.soapui.config.HeaderConfig;
37  import com.eviware.soapui.config.MockResponseConfig;
38  import com.eviware.soapui.impl.actions.ShowDesktopPanelAction;
39  import com.eviware.soapui.impl.wsdl.AbstractWsdlModelItem;
40  import com.eviware.soapui.impl.wsdl.AttachmentContainer;
41  import com.eviware.soapui.impl.wsdl.WsdlAttachmentPart;
42  import com.eviware.soapui.impl.wsdl.WsdlOperation;
43  import com.eviware.soapui.impl.wsdl.actions.mockresponse.CloneMockResponseAction;
44  import com.eviware.soapui.impl.wsdl.actions.mockresponse.DeleteMockResponseAction;
45  import com.eviware.soapui.impl.wsdl.actions.mockresponse.RenameMockResponseAction;
46  import com.eviware.soapui.impl.wsdl.actions.support.ShowOnlineHelpAction;
47  import com.eviware.soapui.impl.wsdl.submit.transports.http.AttachmentUtils;
48  import com.eviware.soapui.impl.wsdl.submit.transports.http.BodyPartAttachment;
49  import com.eviware.soapui.impl.wsdl.submit.transports.http.MimeMessageMockResponseEntity;
50  import com.eviware.soapui.impl.wsdl.submit.transports.http.MockResponseDataSource;
51  import com.eviware.soapui.impl.wsdl.support.FileAttachment;
52  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
53  import com.eviware.soapui.impl.wsdl.support.MessageXmlObject;
54  import com.eviware.soapui.impl.wsdl.support.MessageXmlPart;
55  import com.eviware.soapui.impl.wsdl.support.MockFileAttachment;
56  import com.eviware.soapui.impl.wsdl.support.ModelItemIconAnimator;
57  import com.eviware.soapui.impl.wsdl.support.WsdlAttachment;
58  import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
59  import com.eviware.soapui.model.iface.Attachment;
60  import com.eviware.soapui.model.mock.MockResponse;
61  import com.eviware.soapui.model.mock.MockRunContext;
62  import com.eviware.soapui.settings.WsdlSettings;
63  import com.eviware.soapui.support.Tools;
64  import com.eviware.soapui.support.UISupport;
65  import com.eviware.soapui.support.action.ActionSupport;
66  import com.eviware.soapui.support.types.StringToStringMap;
67  
68  public class WsdlMockResponse extends AbstractWsdlModelItem<MockResponseConfig> implements MockResponse, AttachmentContainer
69  {
70  	private final static Log log = LogFactory.getLog( WsdlMockResponse.class );
71  	
72  	public final static String MOCKRESULT_PROPERTY = WsdlMockResponse.class.getName() + "@mockresult";
73  	public final static String SCRIPT_PROPERTY = WsdlMockResponse.class.getName() + "@script";
74  	public final static String HEADERS_PROPERTY = WsdlMockResponse.class.getName() + "@headers";
75  	public static final String DISABLE_MULTIPART_ATTACHMENTS = WsdlMockResponse.class.getName() + "@disable-multipart-attachments";
76  	
77  	protected List<FileAttachment> attachments = new ArrayList<FileAttachment>();
78  	private List<WsdlAttachmentPart> definedAttachmentParts;
79  	private ModelItemIconAnimator iconAnimator;
80  	private WsdlMockResult mockResult;
81  	
82  	public WsdlMockResponse( WsdlMockOperation operation, MockResponseConfig config )
83  	{
84  		super( config, operation, "/mockResponse.gif" );
85  		
86  		for( AttachmentConfig ac : getConfig().getAttachmentList() )
87  		{
88  			attachments.add( new MockFileAttachment( ac, this ));
89  		}
90  		
91  		if( !config.isSetEncoding() )
92  			config.setEncoding( "UTF-8" );
93  		
94  		addAction( new ShowDesktopPanelAction( "Open MockResponse Editor", "Opens the MockResponse Editor for this MockResponse", this ));
95        addAction( ActionSupport.SEPARATOR_ACTION );
96  //      addAction( new AddMockResponseToTestCaseAction( this ));
97        addAction( new CloneMockResponseAction( this ));
98        addAction( ActionSupport.SEPARATOR_ACTION );
99        addAction( new RenameMockResponseAction( this ));
100       addAction( new DeleteMockResponseAction( this ));
101       addAction( ActionSupport.SEPARATOR_ACTION );
102       addAction( new ShowOnlineHelpAction( HelpUrls.MOCKRESPONSE_HELP_URL ));
103       
104       iconAnimator = new ModelItemIconAnimator( this, "/mockResponse.gif", 
105 					new String[] {"/exec_request_1.gif", "/exec_request_2.gif",
106 					"/exec_request_3.gif", "/exec_request_4.gif"} );
107 	}
108 
109 	public Attachment[] getAttachments()
110 	{
111 		return attachments.toArray( new Attachment[attachments.size()] );
112 	}
113 	
114 	public String getScript()
115 	{
116 		return getConfig().isSetScript() ? getConfig().getScript().getStringValue() : null;
117 	}
118 
119 	public String getEncoding()
120 	{
121 		return getConfig().getEncoding();
122 	}
123 	
124 	public void setEncoding( String encoding )
125 	{
126 		String old = getEncoding();
127 		getConfig().setEncoding( encoding );
128 		notifyPropertyChanged( ENCODING_PROPERTY, old, encoding );
129 	}
130 
131 	public String getResponseContent()
132 	{
133 		return getConfig().getResponseContent();
134 	}
135 
136 	public void setResponseContent( String responseContent )
137 	{
138 		String oldContent = getResponseContent();
139 		if( !responseContent.equals( oldContent ))
140 		{
141 			getConfig().setResponseContent( responseContent );
142 			notifyPropertyChanged( RESPONSECONTENT_PROPERTY, oldContent, responseContent );
143 		}
144 	}
145 	
146 	@Override
147 	public ImageIcon getIcon()
148 	{
149 		return iconAnimator.getIcon();
150 	}
151 
152 	public WsdlMockOperation getMockOperation()
153 	{
154 		return ( WsdlMockOperation ) getParent();
155 	}
156 	
157 	public WsdlMockResult execute( WsdlMockRequest request, WsdlMockResult result ) throws DispatchException
158 	{
159 		try
160 		{
161 			iconAnimator.start();
162 			StringToStringMap responseHeaders = getResponseHeaders();
163 			StringToStringMap contextProperties = request.getContext().toStringToStringMap();
164 			for( String name : responseHeaders.keySet() )
165 			{
166 				result.addHeader( name, Tools.expandProperties( contextProperties, responseHeaders.get( name ), false ));
167 			}
168 			
169 			String responseContent = getResponseContent();
170 			String script = getScript();
171 			if( script != null && script.trim().length() > 0 )
172 			{
173 				evaluateScript( request );
174 				contextProperties = request.getContext().toStringToStringMap();
175 				responseContent = Tools.expandProperties( contextProperties, responseContent, false );
176 			}
177 			
178 			if( !result.isCommitted())
179 			{
180 				responseContent = writeResponse( result, responseContent );
181 			}
182 			
183 			result.setReponseContent( responseContent );
184 			
185 			setMockResult( result );
186 			
187 			return mockResult;
188 		}
189 		catch( Throwable e )
190 		{
191 			throw new DispatchException( e );
192 		}
193 		finally
194 		{
195 			iconAnimator.stop();
196 		}
197 	}
198 
199 	public void evaluateScript( WsdlMockRequest request )
200 	{
201 		String script = getScript();
202 		if( script == null || script.trim().length() == 0 )
203 			return ;
204 			
205 		// unsatisfactory way of handling multiple threads -> create pool of scripts instead
206 		Binding localBinding = new Binding();
207 		
208 		WsdlMockService mockService = getMockOperation().getMockService();
209 		WsdlMockRunner mockRunner = mockService.getMockRunner();
210 		MockRunContext context = mockRunner == null ? new WsdlMockRunContext( mockService ) : mockRunner.getMockContext(); 
211 		
212 		localBinding.setVariable( "context", context );
213 		localBinding.setVariable( "mockRequest", request);
214 		localBinding.setVariable( "mockResponse", this );
215 		localBinding.setVariable( "log", SoapUI.ensureGroovyLog() );
216 		
217 		Script localScript = mockService.getScriptShell().parse( script );
218 		localScript.setBinding( localBinding );
219 		localScript.run();
220 	}
221 
222 	public void setScript( String script )
223 	{
224 		String oldScript = getScript();
225 		if( !script.equals( oldScript ))
226 		{
227 			if( !getConfig().isSetScript())
228 				getConfig().addNewScript();
229 			getConfig().getScript().setStringValue( script );
230 			
231 			notifyPropertyChanged( SCRIPT_PROPERTY, oldScript, script );
232 		}
233 	}
234 
235 	public void setResponseHeaders( StringToStringMap headers )
236 	{
237 		StringToStringMap oldHeaders = getResponseHeaders();
238 		
239 		HeaderConfig [] headerConfigs = new HeaderConfig[headers.size()];
240 		int ix = 0;
241 		for( String header : headers.keySet() )
242 		{
243 			headerConfigs[ix] = HeaderConfig.Factory.newInstance();
244 			headerConfigs[ix].setName( header );
245 			headerConfigs[ix].setValue( headers.get( header ) );
246 			ix++;
247 		}
248 		
249 		getConfig().setHeaderArray( headerConfigs );
250 		
251 		notifyPropertyChanged( HEADERS_PROPERTY, oldHeaders, headers );
252 	}
253 
254 	public StringToStringMap getResponseHeaders()
255 	{
256 		StringToStringMap result = new StringToStringMap();
257 		List<HeaderConfig> headerList = getConfig().getHeaderList();
258 		for( HeaderConfig header : headerList )
259 		{
260 			result.put( header.getName(), header.getValue() );
261 		}
262 		
263 		return result;
264 	}
265 
266 	public Attachment attachFile( File file, boolean cache )
267 	{
268 		try
269 		{
270 			FileAttachment fileAttachment = new MockFileAttachment( file, cache, this );
271 			attachments.add( fileAttachment );
272 			notifyPropertyChanged(ATTACHMENTS_PROPERTY, null, fileAttachment );
273 			return fileAttachment;
274 		}
275 		catch (IOException e)
276 		{
277 			UISupport.showErrorMessage( e );
278 			return null;
279 		}
280 	}
281 
282 	public int getAttachmentCount()
283 	{
284 		return attachments.size();
285 	}
286 
287 	public WsdlAttachment getAttachmentAt( int index )
288 	{
289 		return attachments.get( index );
290 	}
291 
292 	public void removeAttachment( Attachment attachment )
293 	{
294 		int ix = attachments.indexOf( attachment );
295 		attachments.remove( ix );
296 		
297 		try
298 		{
299 			notifyPropertyChanged(ATTACHMENTS_PROPERTY, attachment, null );
300 		}
301 		finally
302 		{
303 			getConfig().removeAttachment( ix);
304 		}
305 	}
306 
307 	public WsdlAttachmentPart[] getDefinedAttachmentParts()
308 	{
309 		if( definedAttachmentParts == null )
310 		{
311 			try
312 			{
313 				WsdlOperation operation = getMockOperation().getOperation();
314 				if( operation == null )
315 				{
316 					definedAttachmentParts = new ArrayList<WsdlAttachmentPart>();
317 				}
318 				else
319 				{
320 					UISupport.setHourglassCursor();
321 					definedAttachmentParts = AttachmentUtils.extractAttachmentParts( 
322 								operation, getResponseContent(), true );
323 				}
324 			}
325 			catch (Exception e)
326 			{
327 				log.warn( e.toString() );
328 			}
329 			finally 
330 			{
331 				UISupport.resetCursor();
332 			}
333 		}
334 		
335 		return definedAttachmentParts.toArray( new WsdlAttachmentPart[definedAttachmentParts.size()] );
336 	}
337 
338 	public WsdlAttachmentPart getAttachmentPart( String partName )
339 	{
340 		WsdlAttachmentPart[] parts = getDefinedAttachmentParts();
341 		for( WsdlAttachmentPart part : parts )
342 		{
343 			if( part.getName().equals( partName ))
344 				return part;
345 		}
346 		
347 		return null;
348 	}
349 
350 	public Attachment[] getAttachmentsForPart( String partName )
351 	{
352 		List<Attachment> result = new ArrayList<Attachment>();
353 		
354 		for( Attachment attachment : attachments )
355 		{
356 			if( attachment.getPart().equals( partName ))
357 				result.add( attachment );
358 		}
359 		
360 		return result.toArray( new Attachment[result.size()]);
361 	}
362 
363 	public boolean isMtomEnabled()
364 	{
365 		return getSettings().getBoolean( WsdlSettings.ENABLE_MTOM );
366 	}
367 	
368 	public void setMtomEnabled( boolean mtomEnabled )
369 	{
370 		boolean old = isMtomEnabled();
371 		getSettings().setBoolean( WsdlSettings.ENABLE_MTOM, mtomEnabled );
372 		definedAttachmentParts = null;
373 		notifyPropertyChanged( MTOM_NABLED_PROPERTY, old, mtomEnabled );
374 	}
375 
376 	private String writeResponse(WsdlMockResult response, String responseContent) throws Exception
377 	{
378 		MimeMultipart mp = null;
379 		WsdlOperation operation = getMockOperation().getOperation();
380 		if( operation == null )
381 			throw new Exception( "Missing WsdlOperation for mock response");
382 		
383 		SoapVersion soapVersion = operation.getInterface().getSoapVersion();
384 		
385 		StringToStringMap contentIds = new StringToStringMap();
386 		boolean isXOP = false;
387 		
388 		// preprocess only if neccessary
389 		if( isMtomEnabled() || getAttachmentCount() > 0 )
390 		{
391 			try
392 			{
393 				mp = new MimeMultipart();
394 				
395 				MessageXmlObject requestXmlObject = new MessageXmlObject(( WsdlOperation ) operation, 
396 							getResponseContent(), true);
397 				MessageXmlPart[] requestParts = requestXmlObject.getMessageParts();
398 				for (MessageXmlPart requestPart : requestParts)
399 				{
400 					if (AttachmentUtils.prepareMessagePart(this, mp, requestPart, contentIds))
401 						isXOP = true;
402 				}
403 				responseContent = requestXmlObject.getMessageContent();
404 			}
405 			catch (Exception e)
406 			{
407 				log.warn( "Failed to process inline/MTOM attachments; " + e );
408 			}			
409 		}
410 		
411 		response.initResponse();
412 		
413 		// non-multipart request?
414 		if( !isXOP && (mp == null || mp.getCount() == 0 ) && getAttachmentCount() == 0 )
415 		{
416 			String encoding = getEncoding();
417 			byte[] content = encoding == null ? responseContent.getBytes() : responseContent.getBytes(encoding);
418 			
419 			response.setContentType( soapVersion.getContentTypeHttpHeader( encoding ));
420 			response.getOutputStream().write( content );
421 		}
422 		else
423 		{
424 			// make sure..
425 			if( mp == null )
426 				mp = new MimeMultipart();
427 			
428 			// init root part
429 			initRootPart(responseContent, mp, isXOP);
430 			
431 			// init mimeparts
432 			AttachmentUtils.addMimeParts(this, mp, contentIds);
433 			
434 			// create request message
435 			MimeMessage message = new MimeMessage( AttachmentUtils.JAVAMAIL_SESSION );
436 			message.setContent( mp );
437 			message.saveChanges();
438 			MimeMessageMockResponseEntity mimeMessageRequestEntity = new MimeMessageMockResponseEntity( message, isXOP, this );
439 			
440 			response.addHeader( "Content-Type", mimeMessageRequestEntity.getContentType() );
441 			response.addHeader( "MIME-Version", "1.0" );
442 			mimeMessageRequestEntity.writeRequest( response.getOutputStream() );
443 		}
444 		
445 		return responseContent;
446 	}
447 	
448 	private void initRootPart(String requestContent, MimeMultipart mp, boolean isXOP) throws MessagingException
449 	{
450 		MimeBodyPart rootPart = new PreencodedMimeBodyPart( "8bit" );
451 		rootPart.setContentID( AttachmentUtils.ROOTPART_SOAPUI_ORG  );
452 		mp.addBodyPart( rootPart, 0 );
453 		
454 		DataHandler dataHandler = new DataHandler( new MockResponseDataSource( this, requestContent, isXOP ) );
455 		rootPart.setDataHandler( dataHandler);
456 	}
457 
458 	public Attachment addAttachment( Attachment attachment )
459 	{
460 		if( attachment instanceof BodyPartAttachment )
461 		{
462 			try
463 			{
464 				BodyPartAttachment att = ( BodyPartAttachment ) attachment;
465 				
466 				AttachmentConfig newConfig = (AttachmentConfig) getConfig().addNewAttachment();
467 				newConfig.setData( Tools.readAll( att.getInputStream(), 0 ).toByteArray() );
468 				newConfig.setContentId( att.getContentID() );
469 				newConfig.setContentType( att.getContentType() );
470 				newConfig.setName( att.getName() );
471 				
472 				FileAttachment newAttachment = new MockFileAttachment( newConfig, this );
473 				attachments.add( newAttachment);
474 				return newAttachment;
475 			}
476 			catch( IOException e )
477 			{
478 				e.printStackTrace();
479 			}
480 		}
481 		else if( attachment instanceof FileAttachment )
482 		{
483 			AttachmentConfig oldConfig = ((FileAttachment)attachment).getConfig();
484 			AttachmentConfig newConfig = (AttachmentConfig) getConfig().addNewAttachment().set( oldConfig );
485 			FileAttachment newAttachment = new MockFileAttachment( newConfig, this );
486 			attachments.add( newAttachment);
487 			return newAttachment;
488 		}
489 
490 		return null;
491 	}
492 	
493 	public void setMockResult( WsdlMockResult mockResult )
494 	{
495 		WsdlMockResult oldResult = this.mockResult;
496 		this.mockResult = mockResult;
497 		notifyPropertyChanged( MOCKRESULT_PROPERTY, oldResult, mockResult );
498 	}
499 
500 	public WsdlMockResult getMockResult()
501 	{
502 		return mockResult;
503 	}
504 	
505 	public boolean isMultipartEnabled()
506 	{
507 		return !getSettings().getBoolean( DISABLE_MULTIPART_ATTACHMENTS );
508 	}
509 	
510 	public void setMultipartEnabled( boolean multipartEnabled )
511 	{
512 		getSettings().setBoolean( DISABLE_MULTIPART_ATTACHMENTS, multipartEnabled );
513 	}
514 }