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.mock;
14  
15  import java.beans.PropertyChangeListener;
16  import java.io.File;
17  import java.io.IOException;
18  import java.util.ArrayList;
19  import java.util.Arrays;
20  import java.util.List;
21  import java.util.zip.GZIPOutputStream;
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  import javax.wsdl.BindingOperation;
31  import javax.wsdl.Message;
32  
33  import org.apache.log4j.Logger;
34  import org.apache.xmlbeans.SchemaGlobalElement;
35  import org.apache.xmlbeans.SchemaType;
36  
37  import com.eviware.soapui.SoapUI;
38  import com.eviware.soapui.config.AttachmentConfig;
39  import com.eviware.soapui.config.HeaderConfig;
40  import com.eviware.soapui.config.MockResponseConfig;
41  import com.eviware.soapui.impl.wsdl.AbstractWsdlModelItem;
42  import com.eviware.soapui.impl.wsdl.AttachmentContainer;
43  import com.eviware.soapui.impl.wsdl.WsdlAttachmentPart;
44  import com.eviware.soapui.impl.wsdl.WsdlContentPart;
45  import com.eviware.soapui.impl.wsdl.WsdlHeaderPart;
46  import com.eviware.soapui.impl.wsdl.WsdlOperation;
47  import com.eviware.soapui.impl.wsdl.WsdlSubmitContext;
48  import com.eviware.soapui.impl.wsdl.submit.filters.PropertyExpansionRequestFilter;
49  import com.eviware.soapui.impl.wsdl.submit.filters.RemoveEmptyContentRequestFilter;
50  import com.eviware.soapui.impl.wsdl.submit.transports.http.AttachmentUtils;
51  import com.eviware.soapui.impl.wsdl.submit.transports.http.BodyPartAttachment;
52  import com.eviware.soapui.impl.wsdl.submit.transports.http.MimeMessageMockResponseEntity;
53  import com.eviware.soapui.impl.wsdl.submit.transports.http.MockResponseDataSource;
54  import com.eviware.soapui.impl.wsdl.support.CompressedStringSupport;
55  import com.eviware.soapui.impl.wsdl.support.FileAttachment;
56  import com.eviware.soapui.impl.wsdl.support.MessageXmlObject;
57  import com.eviware.soapui.impl.wsdl.support.MessageXmlPart;
58  import com.eviware.soapui.impl.wsdl.support.MockFileAttachment;
59  import com.eviware.soapui.impl.wsdl.support.ModelItemIconAnimator;
60  import com.eviware.soapui.impl.wsdl.support.WsdlAttachment;
61  import com.eviware.soapui.impl.wsdl.support.soap.SoapUtils;
62  import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
63  import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlContext;
64  import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils;
65  import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils.SoapHeader;
66  import com.eviware.soapui.model.iface.Attachment;
67  import com.eviware.soapui.model.iface.MessagePart;
68  import com.eviware.soapui.model.mock.MockResponse;
69  import com.eviware.soapui.model.mock.MockRunContext;
70  import com.eviware.soapui.settings.WsdlSettings;
71  import com.eviware.soapui.support.Tools;
72  import com.eviware.soapui.support.UISupport;
73  import com.eviware.soapui.support.scripting.ScriptEnginePool;
74  import com.eviware.soapui.support.scripting.SoapUIScriptEngine;
75  import com.eviware.soapui.support.types.StringToStringMap;
76  import com.eviware.soapui.support.xml.XmlUtils;
77  
78  /***
79   * A WsdlMockResponse contained by a WsdlMockOperation
80   * 
81   * @author ole.matzura
82   */
83  
84  public class WsdlMockResponse extends AbstractWsdlModelItem<MockResponseConfig> implements MockResponse,
85  			AttachmentContainer
86  {
87  	private final static Logger log = Logger.getLogger( WsdlMockResponse.class );
88  
89  	public final static String MOCKRESULT_PROPERTY = WsdlMockResponse.class.getName() + "@mockresult";
90  	public final static String SCRIPT_PROPERTY = WsdlMockResponse.class.getName() + "@script";
91  	public final static String HEADERS_PROPERTY = WsdlMockResponse.class.getName() + "@headers";
92  	public final static String DISABLE_MULTIPART_ATTACHMENTS = WsdlMockResponse.class.getName()
93  				+ "@disable-multipart-attachments";
94  	public static final String FORCE_MTOM = WsdlMockResponse.class.getName() + "@force_mtom";
95  	public final static String RESPONSE_DELAY_PROPERTY = WsdlMockResponse.class.getName() + "@response-delay";
96  	public static final String STRIP_WHITESPACES = WsdlMockResponse.class.getName() + "@strip-whitespaces";
97  	public static final String REMOVE_EMPTY_CONTENT = WsdlMockResponse.class.getName() + "@remove_empty_content";
98  	public static final String ENCODE_ATTACHMENTS = WsdlMockResponse.class.getName() + "@encode_attachments";
99  	public static final String RESPONSE_HTTP_STATUS = WsdlMockResponse.class.getName() + "@response-http-status";
100 
101 	protected List<FileAttachment> attachments = new ArrayList<FileAttachment>();
102 	private List<WsdlAttachmentPart> definedAttachmentParts;
103 	private ModelItemIconAnimator iconAnimator;
104 	private WsdlMockResult mockResult;
105 	private String responseContent;
106 	private ScriptEnginePool scriptEnginePool;
107 
108 	public WsdlMockResponse( WsdlMockOperation operation, MockResponseConfig config )
109 	{
110 		super( config, operation, "/mockResponse.gif" );
111 
112 		for( AttachmentConfig ac : getConfig().getAttachmentList() )
113 		{
114 			attachments.add( new MockFileAttachment( ac, this ) );
115 		}
116 
117 		if( !config.isSetEncoding() )
118 			config.setEncoding( "UTF-8" );
119 
120 		iconAnimator = new ModelItemIconAnimator( this, "/mockResponse.gif", new String[] { "/exec_request_1.gif",
121 					"/exec_request_2.gif", "/exec_request_3.gif", "/exec_request_4.gif" } );
122 
123 		scriptEnginePool = new ScriptEnginePool( this );
124 		scriptEnginePool.setScript( getScript() );
125 	}
126 
127 	public Attachment[] getAttachments()
128 	{
129 		return attachments.toArray( new Attachment[attachments.size()] );
130 	}
131 
132 	public String getScript()
133 	{
134 		return getConfig().isSetScript() ? getConfig().getScript().getStringValue() : null;
135 	}
136 
137 	public String getEncoding()
138 	{
139 		return getConfig().getEncoding();
140 	}
141 
142 	public void setEncoding( String encoding )
143 	{
144 		String old = getEncoding();
145 		getConfig().setEncoding( encoding );
146 		notifyPropertyChanged( ENCODING_PROPERTY, old, encoding );
147 	}
148 
149 	public String getResponseContent()
150 	{
151 		if( getConfig().getResponseContent() == null )
152 			getConfig().addNewResponseContent();
153 
154 		if( responseContent == null )
155 			responseContent = CompressedStringSupport.getString( getConfig().getResponseContent() );
156 
157 		return responseContent;
158 	}
159 
160 	public void setResponseContent( String responseContent )
161 	{
162 		String oldContent = getResponseContent();
163 		if( !responseContent.equals( oldContent ) )
164 		{
165 			this.responseContent = responseContent;
166 			notifyPropertyChanged( RESPONSECONTENT_PROPERTY, oldContent, responseContent );
167 		}
168 	}
169 
170 	@Override
171 	public ImageIcon getIcon()
172 	{
173 		return iconAnimator.getIcon();
174 	}
175 
176 	public WsdlMockOperation getMockOperation()
177 	{
178 		return ( WsdlMockOperation ) getParent();
179 	}
180 
181 	public WsdlMockResult execute( WsdlMockRequest request, WsdlMockResult result ) throws DispatchException
182 	{
183 		try
184 		{
185 			iconAnimator.start();
186 
187 			long delay = getResponseDelay();
188 			if( delay > 0 )
189 				Thread.sleep( delay );
190 
191 			String responseContent = getResponseContent();
192 			String script = getScript();
193 			if( script != null && script.trim().length() > 0 )
194 			{
195 				evaluateScript( request );
196 			}
197 			
198 			// create merged context
199 			WsdlSubmitContext context = new WsdlSubmitContext();
200 			context.putAll( request.getContext() );
201 			context.putAll( request.getRequestContext() );
202 			
203 			StringToStringMap responseHeaders = getResponseHeaders();
204 			for( String name : responseHeaders.keySet() )
205 			{
206 				result.addHeader( name, PropertyExpansionRequestFilter.expandProperties( context,
207 							responseHeaders.get( name ) ) );
208 			}
209 
210 			responseContent = PropertyExpansionRequestFilter.expandProperties( context, responseContent );
211 
212 			if( !result.isCommitted() )
213 			{
214 				responseContent = writeResponse( result, responseContent );
215 			}
216 
217 			result.setResponseContent( responseContent );
218 			
219 			setMockResult( result );
220 
221 			return mockResult;
222 		}
223 		catch( Throwable e )
224 		{
225 			throw new DispatchException( e );
226 		}
227 		finally
228 		{
229 			iconAnimator.stop();
230 		}
231 	}
232 
233 	public void evaluateScript( WsdlMockRequest request ) throws Exception
234 	{
235 		String script = getScript();
236 		if( script == null || script.trim().length() == 0 )
237 			return;
238 
239 		// unsatisfactory way of handling multiple threads -> create pool of
240 		// scripts instead
241 		WsdlMockService mockService = getMockOperation().getMockService();
242 		WsdlMockRunner mockRunner = mockService.getMockRunner();
243 		MockRunContext context = mockRunner == null ? new WsdlMockRunContext( mockService, null ) : mockRunner
244 					.getMockContext();
245 
246 		SoapUIScriptEngine scriptEngine = scriptEnginePool.getScriptEngine();
247 
248 		try
249 		{
250 			scriptEngine.setVariable( "context", context );
251 			scriptEngine.setVariable( "requestContext", request == null ? null : request.getRequestContext() );
252 			scriptEngine.setVariable( "mockContext", context );
253 			scriptEngine.setVariable( "mockRequest", request );
254 			scriptEngine.setVariable( "mockResponse", this );
255 			scriptEngine.setVariable( "log", SoapUI.ensureGroovyLog() );
256 
257 			scriptEngine.run();
258 		}
259 		catch( RuntimeException e )
260 		{
261 			throw new Exception( e.getMessage(), e );
262 		}
263 		finally
264 		{
265 			scriptEnginePool.returnScriptEngine( scriptEngine );
266 		}
267 	}
268 
269 	@Override
270 	public void release()
271 	{
272 		super.release();
273 		scriptEnginePool.release();
274 	}
275 
276 	public void setScript( String script )
277 	{
278 		String oldScript = getScript();
279 		if( !script.equals( oldScript ) )
280 		{
281 			if( !getConfig().isSetScript() )
282 				getConfig().addNewScript();
283 			getConfig().getScript().setStringValue( script );
284 
285 			scriptEnginePool.setScript( script );
286 
287 			notifyPropertyChanged( SCRIPT_PROPERTY, oldScript, script );
288 		}
289 	}
290 
291 	public void setResponseHeaders( StringToStringMap headers )
292 	{
293 		StringToStringMap oldHeaders = getResponseHeaders();
294 
295 		HeaderConfig[] headerConfigs = new HeaderConfig[headers.size()];
296 		int ix = 0;
297 		for( String header : headers.keySet() )
298 		{
299 			headerConfigs[ix] = HeaderConfig.Factory.newInstance();
300 			headerConfigs[ix].setName( header );
301 			headerConfigs[ix].setValue( headers.get( header ) );
302 			ix++;
303 		}
304 
305 		getConfig().setHeaderArray( headerConfigs );
306 
307 		notifyPropertyChanged( HEADERS_PROPERTY, oldHeaders, headers );
308 	}
309 
310 	public StringToStringMap getResponseHeaders()
311 	{
312 		StringToStringMap result = new StringToStringMap();
313 		List<HeaderConfig> headerList = getConfig().getHeaderList();
314 		for( HeaderConfig header : headerList )
315 		{
316 			result.put( header.getName(), header.getValue() );
317 		}
318 
319 		return result;
320 	}
321 
322 	public MessagePart[] getResponseParts()
323 	{
324 		try
325 		{
326 			// init
327 			WsdlOperation op = getMockOperation().getOperation();
328 			if( op == null || op.isOneWay() )
329 				return new MessagePart[0];
330 			
331 			List<MessagePart> result = new ArrayList<MessagePart>();
332 			WsdlContext wsdlContext = op.getInterface().getWsdlContext();
333 			BindingOperation bindingOperation = op.findBindingOperation( wsdlContext.getDefinition() );
334 
335 			if( bindingOperation == null )
336 				return new MessagePart[0];
337 
338 			// header parts
339 			List<SoapHeader> headers = WsdlUtils.getSoapHeaders( bindingOperation.getBindingOutput()
340 						.getExtensibilityElements() );
341 
342 			for( int i = 0; i < headers.size(); i++ )
343 			{
344 				SoapHeader header = headers.get( i );
345 
346 				Message message = wsdlContext.getDefinition().getMessage( header.getMessage() );
347 				if( message == null )
348 				{
349 					log.error( "Missing message for header: " + header.getMessage() );
350 					continue;
351 				}
352 
353 				javax.wsdl.Part part = message.getPart( header.getPart() );
354 
355 				if( part != null )
356 				{
357 					SchemaType schemaType = WsdlUtils.getSchemaTypeForPart( wsdlContext, part );
358 					SchemaGlobalElement schemaElement = WsdlUtils.getSchemaElementForPart( wsdlContext, part );
359 					if( schemaType != null )
360 						result.add( new WsdlHeaderPart( part.getName(), schemaType, part.getElementName(), schemaElement ) );
361 				}
362 				else
363 					log.error( "Missing part for header; " + header.getPart() );
364 			}
365 
366 			// content parts
367 			javax.wsdl.Part[] parts = WsdlUtils.getOutputParts( bindingOperation );
368 
369 			for( int i = 0; i < parts.length; i++ )
370 			{
371 				javax.wsdl.Part part = parts[i];
372 
373 				if( !WsdlUtils.isAttachmentOutputPart( part, bindingOperation ) )
374 				{
375 					SchemaType schemaType = WsdlUtils.getSchemaTypeForPart( wsdlContext, part );
376 					SchemaGlobalElement schemaElement = WsdlUtils.getSchemaElementForPart( wsdlContext, part );
377 					if( schemaType != null )
378 						result.add( new WsdlContentPart( part.getName(), schemaType, part.getElementName(), schemaElement ) );
379 				}
380 			}
381 
382 			result.addAll( Arrays.asList( getDefinedAttachmentParts() ) );
383 
384 			return result.toArray( new MessagePart[result.size()] );
385 		}
386 		catch( Exception e )
387 		{
388 			SoapUI.logError( e );
389 			return new MessagePart[0];
390 		}
391 	}
392 
393 	public Attachment attachFile( File file, boolean cache )
394 	{
395 		try
396 		{
397 			FileAttachment fileAttachment = new MockFileAttachment( file, cache, this );
398 			attachments.add( fileAttachment );
399 			notifyPropertyChanged( ATTACHMENTS_PROPERTY, null, fileAttachment );
400 			return fileAttachment;
401 		}
402 		catch( IOException e )
403 		{
404 			UISupport.showErrorMessage( e );
405 			return null;
406 		}
407 	}
408 
409 	public int getAttachmentCount()
410 	{
411 		return attachments.size();
412 	}
413 
414 	public WsdlAttachment getAttachmentAt( int index )
415 	{
416 		return attachments.get( index );
417 	}
418 
419 	public void removeAttachment( Attachment attachment )
420 	{
421 		int ix = attachments.indexOf( attachment );
422 		attachments.remove( ix );
423 
424 		try
425 		{
426 			notifyPropertyChanged( ATTACHMENTS_PROPERTY, attachment, null );
427 		}
428 		finally
429 		{
430 			getConfig().removeAttachment( ix );
431 		}
432 	}
433 
434 	public WsdlAttachmentPart[] getDefinedAttachmentParts()
435 	{
436 		if( definedAttachmentParts == null )
437 		{
438 			try
439 			{
440 				WsdlOperation operation = getMockOperation().getOperation();
441 				if( operation == null )
442 				{
443 					definedAttachmentParts = new ArrayList<WsdlAttachmentPart>();
444 				}
445 				else
446 				{
447 					UISupport.setHourglassCursor();
448 					definedAttachmentParts = AttachmentUtils.extractAttachmentParts( operation, getResponseContent(), true,
449 								true );
450 				}
451 			}
452 			catch( Exception e )
453 			{
454 				log.warn( e.toString() );
455 			}
456 			finally
457 			{
458 				UISupport.resetCursor();
459 			}
460 		}
461 
462 		return definedAttachmentParts.toArray( new WsdlAttachmentPart[definedAttachmentParts.size()] );
463 	}
464 
465 	public WsdlAttachmentPart getAttachmentPart( String partName )
466 	{
467 		WsdlAttachmentPart[] parts = getDefinedAttachmentParts();
468 		for( WsdlAttachmentPart part : parts )
469 		{
470 			if( part.getName().equals( partName ) )
471 				return part;
472 		}
473 
474 		return null;
475 	}
476 
477 	public Attachment[] getAttachmentsForPart( String partName )
478 	{
479 		List<Attachment> result = new ArrayList<Attachment>();
480 
481 		for( Attachment attachment : attachments )
482 		{
483 			if( attachment.getPart().equals( partName ) )
484 				result.add( attachment );
485 		}
486 
487 		return result.toArray( new Attachment[result.size()] );
488 	}
489 
490 	public boolean isMtomEnabled()
491 	{
492 		return getSettings().getBoolean( WsdlSettings.ENABLE_MTOM );
493 	}
494 
495 	public void setMtomEnabled( boolean mtomEnabled )
496 	{
497 		boolean old = isMtomEnabled();
498 		getSettings().setBoolean( WsdlSettings.ENABLE_MTOM, mtomEnabled );
499 		definedAttachmentParts = null;
500 		notifyPropertyChanged( MTOM_NABLED_PROPERTY, old, mtomEnabled );
501 	}
502 
503 	private String writeResponse( WsdlMockResult response, String responseContent ) throws Exception
504 	{
505 		MimeMultipart mp = null;
506 		WsdlOperation operation = getMockOperation().getOperation();
507 		if( operation == null )
508 			throw new Exception( "Missing WsdlOperation for mock response" );
509 
510 		SoapVersion soapVersion = operation.getInterface().getSoapVersion();
511 
512 		StringToStringMap contentIds = new StringToStringMap();
513 		boolean isXOP = isMtomEnabled() && isForceMtom();
514 
515 		// preprocess only if neccessary
516 		if( isMtomEnabled() || getAttachmentCount() > 0 )
517 		{
518 			try
519 			{
520 				mp = new MimeMultipart();
521 
522 				MessageXmlObject requestXmlObject = new MessageXmlObject( ( WsdlOperation ) operation,
523 							getResponseContent(), true );
524 				MessageXmlPart[] requestParts = requestXmlObject.getMessageParts();
525 				for( MessageXmlPart requestPart : requestParts )
526 				{
527 					if( AttachmentUtils.prepareMessagePart( this, mp, requestPart, contentIds ) )
528 						isXOP = true;
529 				}
530 				responseContent = requestXmlObject.getMessageContent();
531 			}
532 			catch( Exception e )
533 			{
534 				//log.warn( "Failed to process inline/MTOM attachments; " + e );
535 			}
536 		}
537 
538 		if( isRemoveEmptyContent() )
539 		{
540 			responseContent = RemoveEmptyContentRequestFilter.removeEmptyContent( responseContent );
541 		}
542 		
543 		if( isStripWhitespaces() )
544 		{
545 			responseContent = XmlUtils.stripWhitespaces( responseContent );
546 		}
547 		
548 		String status = getResponseHttpStatus();
549 		WsdlMockRequest request = response.getMockRequest();
550 		
551 		if( status == null || status.trim().length() == 0 )
552 		{
553 			if( SoapUtils.isSoapFault( responseContent, request.getSoapVersion() ))
554 			{
555 				request.getHttpResponse().setStatus( 500 );
556 			}
557 			else
558 			{
559 				request.getHttpResponse().setStatus( 200 );
560 			}
561 		}
562 		else
563 		{
564 			try
565 			{
566 				request.getHttpResponse().setStatus( Integer.parseInt( status ) );
567 			}
568 			catch( RuntimeException e )
569 			{
570 				SoapUI.logError( e );
571 			}
572 		}
573 
574 		// non-multipart request?
575 		if( !isXOP && ( mp == null || mp.getCount() == 0 ) && getAttachmentCount() == 0 )
576 		{
577 			String encoding = getEncoding();
578 			byte[] content = encoding == null ? responseContent.getBytes() : responseContent.getBytes( encoding );
579 
580 			response.setContentType( soapVersion.getContentTypeHttpHeader( encoding, null ) );
581 
582 			String acceptEncoding = response.getMockRequest().getRequestHeaders().get( "Accept-Encoding" );
583 			if( acceptEncoding != null && acceptEncoding.toUpperCase().contains( "GZIP" ) )
584 			{
585 				response.addHeader( "Content-Encoding", "gzip" );
586 				GZIPOutputStream zipOut = new GZIPOutputStream( response.getOutputStream() );
587 				zipOut.write( content );
588 				zipOut.close();
589 			}
590 			else
591 			{
592 				response.getOutputStream().write( content );
593 			}
594 		}
595 		else
596 		{
597 			// make sure..
598 			if( mp == null )
599 				mp = new MimeMultipart();
600 
601 			// init root part
602 			initRootPart( responseContent, mp, isXOP );
603 
604 			// init mimeparts
605 			AttachmentUtils.addMimeParts( this, mp, contentIds );
606 
607 			// create request message
608 			MimeMessage message = new MimeMessage( AttachmentUtils.JAVAMAIL_SESSION );
609 			message.setContent( mp );
610 			message.saveChanges();
611 			MimeMessageMockResponseEntity mimeMessageRequestEntity = new MimeMessageMockResponseEntity( message, isXOP,
612 						this );
613 
614 			response.addHeader( "Content-Type", mimeMessageRequestEntity.getContentType() );
615 			response.addHeader( "MIME-Version", "1.0" );
616 			mimeMessageRequestEntity.writeRequest( response.getOutputStream() );
617 		}
618 
619 		return responseContent;
620 	}
621 
622 	private void initRootPart( String requestContent, MimeMultipart mp, boolean isXOP ) throws MessagingException
623 	{
624 		MimeBodyPart rootPart = new PreencodedMimeBodyPart( "8bit" );
625 		rootPart.setContentID( AttachmentUtils.ROOTPART_SOAPUI_ORG );
626 		mp.addBodyPart( rootPart, 0 );
627 
628 		DataHandler dataHandler = new DataHandler( new MockResponseDataSource( this, requestContent, isXOP ) );
629 		rootPart.setDataHandler( dataHandler );
630 	}
631 
632 	public Attachment addAttachment( Attachment attachment )
633 	{
634 		if( attachment instanceof BodyPartAttachment )
635 		{
636 			try
637 			{
638 				BodyPartAttachment att = ( BodyPartAttachment ) attachment;
639 
640 				AttachmentConfig newConfig = ( AttachmentConfig ) getConfig().addNewAttachment();
641 				newConfig.setData( Tools.readAll( att.getInputStream(), 0 ).toByteArray() );
642 				newConfig.setContentId( att.getContentID() );
643 				newConfig.setContentType( att.getContentType() );
644 				newConfig.setName( att.getName() );
645 
646 				FileAttachment newAttachment = new MockFileAttachment( newConfig, this );
647 				attachments.add( newAttachment );
648 				return newAttachment;
649 			}
650 			catch( Exception e )
651 			{
652 				SoapUI.logError( e );
653 			}
654 		}
655 		else if( attachment instanceof FileAttachment )
656 		{
657 			AttachmentConfig oldConfig = ( ( FileAttachment ) attachment ).getConfig();
658 			AttachmentConfig newConfig = ( AttachmentConfig ) getConfig().addNewAttachment().set( oldConfig );
659 			FileAttachment newAttachment = new MockFileAttachment( newConfig, this );
660 			attachments.add( newAttachment );
661 			return newAttachment;
662 		}
663 
664 		return null;
665 	}
666 
667 	public void setResponseDelay( long delay )
668 	{
669 		long oldDelay = getResponseDelay();
670 
671 		if( delay == 0 )
672 			getSettings().clearSetting( RESPONSE_DELAY_PROPERTY );
673 		else
674 			getSettings().setLong( RESPONSE_DELAY_PROPERTY, delay );
675 
676 		notifyPropertyChanged( RESPONSE_DELAY_PROPERTY, oldDelay, delay );
677 	}
678 
679 	public long getResponseDelay()
680 	{
681 		return getSettings().getLong( RESPONSE_DELAY_PROPERTY, 0 );
682 	}
683 	
684 	public void setResponseHttpStatus( String httpStatus )
685 	{
686 		String oldStatus = getResponseHttpStatus();
687 
688 		getConfig().setHttpResponseStatus( httpStatus );
689 
690 		notifyPropertyChanged( RESPONSE_HTTP_STATUS, oldStatus, httpStatus );
691 	}
692 
693 	public String getResponseHttpStatus()
694 	{
695 		return getConfig().getHttpResponseStatus();
696 	}
697 
698 	public void setMockResult( WsdlMockResult mockResult )
699 	{
700 		WsdlMockResult oldResult = this.mockResult;
701 		this.mockResult = mockResult;
702 		notifyPropertyChanged( MOCKRESULT_PROPERTY, oldResult, mockResult );
703 	}
704 
705 	public WsdlMockResult getMockResult()
706 	{
707 		return mockResult;
708 	}
709 
710 	public long getContentLength()
711 	{
712 		return getResponseContent().length();
713 	}
714 
715 	public boolean isMultipartEnabled()
716 	{
717 		return !getSettings().getBoolean( DISABLE_MULTIPART_ATTACHMENTS );
718 	}
719 
720 	public void setMultipartEnabled( boolean multipartEnabled )
721 	{
722 		getSettings().setBoolean( DISABLE_MULTIPART_ATTACHMENTS, multipartEnabled );
723 	}
724 
725 	public boolean isForceMtom()
726 	{
727 		return getSettings().getBoolean( FORCE_MTOM );
728 	}
729 	
730 	public void setForceMtom( boolean forceMtom )
731    {
732    	boolean old = getSettings().getBoolean( FORCE_MTOM );
733    	getSettings().setBoolean( FORCE_MTOM, forceMtom );
734    	notifyPropertyChanged( FORCE_MTOM, old, forceMtom );
735    }
736 	
737 	public boolean isRemoveEmptyContent()
738 	{
739 		return getSettings().getBoolean( REMOVE_EMPTY_CONTENT );
740 	}
741 
742 	public void setRemoveEmptyContent( boolean removeEmptyContent )
743 	{
744 		boolean old = getSettings().getBoolean( REMOVE_EMPTY_CONTENT );
745 		getSettings().setBoolean( REMOVE_EMPTY_CONTENT, removeEmptyContent );
746 		notifyPropertyChanged( REMOVE_EMPTY_CONTENT, old, removeEmptyContent );
747 	}
748 
749 	public boolean isEncodeAttachments()
750 	{
751 		return getSettings().getBoolean( ENCODE_ATTACHMENTS );
752 	}
753 
754 	public void setEncodeAttachments( boolean encodeAttachments )
755 	{
756 		boolean old = getSettings().getBoolean( ENCODE_ATTACHMENTS );
757 		getSettings().setBoolean( ENCODE_ATTACHMENTS, encodeAttachments );
758 		notifyPropertyChanged( ENCODE_ATTACHMENTS, old, encodeAttachments );
759 	}
760 
761 	public boolean isStripWhitespaces()
762 	{
763 		return getSettings().getBoolean( STRIP_WHITESPACES );
764 	}
765 
766 	public void setStripWhitespaces( boolean stripWhitespaces )
767 	{
768 		boolean old = getSettings().getBoolean( STRIP_WHITESPACES );
769 		getSettings().setBoolean( STRIP_WHITESPACES, stripWhitespaces );
770 		notifyPropertyChanged( STRIP_WHITESPACES, old, stripWhitespaces );
771 	}
772 
773 	@Override
774 	public void onSave()
775 	{
776 		if( responseContent != null )
777 		{
778 			CompressedStringSupport.setString( getConfig().getResponseContent(), responseContent );
779 			responseContent = null;
780 		}
781 	}
782 
783 	public void addAttachmentsChangeListener( PropertyChangeListener listener )
784 	{
785 		addPropertyChangeListener( ATTACHMENTS_PROPERTY, listener );
786 	}
787 
788 	public boolean isReadOnly()
789 	{
790 		return false;
791 	}
792 
793 	public void removeAttachmentsChangeListener( PropertyChangeListener listener )
794 	{
795 		removePropertyChangeListener( ATTACHMENTS_PROPERTY, listener );
796 	}
797 
798 	public SoapVersion getSoapVersion()
799 	{
800 		return getMockOperation().getOperation() == null ? SoapVersion.Soap11 : 
801 			 getMockOperation().getOperation().getInterface().getSoapVersion();
802 	}
803 }