View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2010 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.teststeps;
14  
15  import java.beans.PropertyChangeEvent;
16  import java.util.ArrayList;
17  import java.util.Collection;
18  import java.util.HashMap;
19  import java.util.List;
20  import java.util.Map;
21  
22  import javax.swing.ImageIcon;
23  import javax.xml.namespace.QName;
24  
25  import org.apache.log4j.Logger;
26  
27  import com.eviware.soapui.SoapUI;
28  import com.eviware.soapui.config.RestRequestStepConfig;
29  import com.eviware.soapui.config.TestStepConfig;
30  import com.eviware.soapui.impl.rest.RestMethod;
31  import com.eviware.soapui.impl.rest.RestRequest;
32  import com.eviware.soapui.impl.rest.RestResource;
33  import com.eviware.soapui.impl.rest.RestService;
34  import com.eviware.soapui.impl.rest.support.RestRequestConverter;
35  import com.eviware.soapui.impl.support.AbstractHttpRequest;
36  import com.eviware.soapui.impl.wsdl.AbstractWsdlModelItem;
37  import com.eviware.soapui.impl.wsdl.WsdlProject;
38  import com.eviware.soapui.impl.wsdl.WsdlSubmit;
39  import com.eviware.soapui.impl.wsdl.submit.transports.http.HttpResponse;
40  import com.eviware.soapui.impl.wsdl.support.assertions.AssertedXPathsContainer;
41  import com.eviware.soapui.impl.wsdl.testcase.WsdlTestCase;
42  import com.eviware.soapui.impl.wsdl.testcase.WsdlTestRunContext;
43  import com.eviware.soapui.impl.wsdl.teststeps.assertions.TestAssertionRegistry.AssertableType;
44  import com.eviware.soapui.impl.wsdl.teststeps.registry.RestRequestStepFactory.ItemDeletedException;
45  import com.eviware.soapui.model.ModelItem;
46  import com.eviware.soapui.model.iface.Interface;
47  import com.eviware.soapui.model.iface.Operation;
48  import com.eviware.soapui.model.iface.Submit;
49  import com.eviware.soapui.model.iface.Request.SubmitException;
50  import com.eviware.soapui.model.project.Project;
51  import com.eviware.soapui.model.propertyexpansion.PropertyExpander;
52  import com.eviware.soapui.model.propertyexpansion.PropertyExpansion;
53  import com.eviware.soapui.model.propertyexpansion.PropertyExpansionsResult;
54  import com.eviware.soapui.model.support.DefaultTestStepProperty;
55  import com.eviware.soapui.model.support.InterfaceListenerAdapter;
56  import com.eviware.soapui.model.support.ModelSupport;
57  import com.eviware.soapui.model.support.ProjectListenerAdapter;
58  import com.eviware.soapui.model.support.TestPropertyListenerAdapter;
59  import com.eviware.soapui.model.support.TestStepBeanProperty;
60  import com.eviware.soapui.model.testsuite.AssertionError;
61  import com.eviware.soapui.model.testsuite.AssertionsListener;
62  import com.eviware.soapui.model.testsuite.TestAssertion;
63  import com.eviware.soapui.model.testsuite.TestCaseRunContext;
64  import com.eviware.soapui.model.testsuite.TestCaseRunner;
65  import com.eviware.soapui.model.testsuite.TestStep;
66  import com.eviware.soapui.model.testsuite.TestStepProperty;
67  import com.eviware.soapui.model.testsuite.TestStepResult;
68  import com.eviware.soapui.model.testsuite.TestStepResult.TestStepStatus;
69  import com.eviware.soapui.support.UISupport;
70  import com.eviware.soapui.support.resolver.ChangeRestMethodResolver;
71  import com.eviware.soapui.support.resolver.ImportInterfaceResolver;
72  import com.eviware.soapui.support.resolver.RemoveTestStepResolver;
73  import com.eviware.soapui.support.resolver.ResolveContext;
74  import com.eviware.soapui.support.resolver.ResolveContext.PathToResolve;
75  import com.eviware.soapui.support.types.StringToStringsMap;
76  
77  public class RestTestRequestStep extends WsdlTestStepWithProperties implements RestTestRequestStepInterface
78  {
79  	private final static Logger log = Logger.getLogger( RestTestRequestStep.class );
80  	private RestRequestStepConfig restRequestStepConfig;
81  	private RestTestRequest testRequest;
82  	private RestResource restResource;
83  	private RestMethod restMethod;
84  	private final InternalProjectListener projectListener = new InternalProjectListener();
85  	private final InternalInterfaceListener interfaceListener = new InternalInterfaceListener();
86  	private WsdlSubmit<RestRequest> submit;
87  	// private final Set<String> requestProperties = new HashSet<String>();
88  	private final Map<String, RestTestStepProperty> requestProperties = new HashMap<String, RestTestStepProperty>();
89  
90  	public RestTestRequestStep( WsdlTestCase testCase, TestStepConfig config, boolean forLoadTest )
91  			throws ItemDeletedException
92  	{
93  		super( testCase, config, true, forLoadTest );
94  
95  		if( getConfig().getConfig() != null )
96  		{
97  			restRequestStepConfig = ( RestRequestStepConfig )getConfig().getConfig().changeType(
98  					RestRequestStepConfig.type );
99  
100 			testRequest = buildTestRequest( forLoadTest );
101 			if( testRequest == null )
102 				throw new ItemDeletedException();
103 
104 			// testRequest = new RestTestRequest( null,
105 			// requestStepConfig.getRestRequest(), this, forLoadTest );
106 			testRequest.addPropertyChangeListener( this );
107 			testRequest.addTestPropertyListener( new InternalTestPropertyListener() );
108 
109 			if( config.isSetName() )
110 				testRequest.setName( config.getName() );
111 			else
112 				config.setName( testRequest.getName() );
113 		}
114 		else
115 		{
116 			restRequestStepConfig = ( RestRequestStepConfig )getConfig().addNewConfig().changeType(
117 					RestRequestStepConfig.type );
118 		}
119 
120 		// Add request properties
121 		refreshRequestProperties();
122 
123 		// init default properties
124 		addProperty( new TestStepBeanProperty( "Endpoint", false, testRequest, "endpoint", this ) );
125 		addProperty( new TestStepBeanProperty( "Username", false, testRequest, "username", this ) );
126 		addProperty( new TestStepBeanProperty( "Password", false, testRequest, "password", this ) );
127 		addProperty( new TestStepBeanProperty( "Domain", false, testRequest, "domain", this ) );
128 
129 		// init properties
130 		addProperty( new TestStepBeanProperty( "Request", false, testRequest, "requestContent", this )
131 		{
132 			@Override
133 			public String getDefaultValue()
134 			{
135 				return createDefaultRequestContent();
136 			}
137 		} );
138 
139 		addProperty( new TestStepBeanProperty( WsdlTestStepWithProperties.RESPONSE_AS_XML, true, testRequest,
140 				"responseContentAsXml", this )
141 		{
142 			@Override
143 			public String getDefaultValue()
144 			{
145 				return createDefaultResponseXmlContent();
146 			}
147 		} );
148 
149 		addProperty( new TestStepBeanProperty( "Response", true, testRequest, "responseContentAsString", this )
150 		{
151 			@Override
152 			public String getDefaultValue()
153 			{
154 				return createDefaultRawResponseContent();
155 			}
156 		} );
157 
158 		addProperty( new DefaultTestStepProperty( "RawRequest", true, this )
159 		{
160 			@Override
161 			public String getValue()
162 			{
163 				HttpResponse response = testRequest.getResponse();
164 				return response == null ? null : response.getRequestContent();
165 			}
166 		} );
167 
168 		initRestTestRequest();
169 
170 		if( !forLoadTest && restResource != null )
171 		{
172 			getResource().getService().getProject().addProjectListener( projectListener );
173 			getResource().getService().addInterfaceListener( interfaceListener );
174 			getResource().getService().addPropertyChangeListener( this );
175 			getResource().addPropertyChangeListener( this );
176 		}
177 
178 		if( getRestMethod() != null )
179 		{
180 			getRestMethod().addPropertyChangeListener( this );
181 		}
182 	}
183 
184 	private void refreshRequestProperties()
185 	{
186 		for( String key : requestProperties.keySet() )
187 		{
188 			deleteProperty( key, true );
189 		}
190 		requestProperties.clear();
191 
192 		for( String key : testRequest.getProperties().keySet() )
193 		{
194 			requestProperties.put( key, new RestTestStepProperty( key ) );
195 			addProperty( requestProperties.get( key ), true );
196 		}
197 	}
198 
199 	@Override
200 	public void beforeSave()
201 	{
202 		super.beforeSave();
203 
204 		if( testRequest != null )
205 			testRequest.beforeSave();
206 	}
207 
208 	@Override
209 	public String getDescription()
210 	{
211 		return testRequest == null ? "<missing>" : testRequest.getDescription();
212 	}
213 
214 	public RestRequestStepConfig getRequestStepConfig()
215 	{
216 		return restRequestStepConfig;
217 	}
218 
219 	protected RestTestRequest buildTestRequest( boolean forLoadTest )
220 	{
221 		if( getRestMethod() == null )
222 			return null;
223 		return new RestTestRequest( getRestMethod(), getRequestStepConfig().getRestRequest(), this, forLoadTest );
224 	}
225 
226 	private void initRestTestRequest()
227 	{
228 		if( getRestMethod() == null )
229 			setDisabled( true );
230 		else
231 			getTestRequest().setRestMethod( getRestMethod() );
232 	}
233 
234 	public String getService()
235 	{
236 		return getRequestStepConfig().getService();
237 	}
238 
239 	public String getResourcePath()
240 	{
241 		return getRequestStepConfig().getResourcePath();
242 	}
243 
244 	protected String createDefaultRawResponseContent()
245 	{
246 		return getResource() == null ? null : getResource().createResponse( true );
247 	}
248 
249 	protected String createDefaultResponseXmlContent()
250 	{
251 		return getResource() == null ? null : getResource().createResponse( true );
252 	}
253 
254 	protected String createDefaultRequestContent()
255 	{
256 		return getResource() == null ? null : getResource().createRequest( true );
257 	}
258 
259 	@Override
260 	public Collection<Interface> getRequiredInterfaces()
261 	{
262 		ArrayList<Interface> result = new ArrayList<Interface>();
263 		result.add( findRestResource().getInterface() );
264 		return result;
265 	}
266 
267 	private RestResource findRestResource()
268 	{
269 		Project project = ModelSupport.getModelItemProject( this );
270 		RestService restService = ( RestService )project.getInterfaceByName( getRequestStepConfig().getService() );
271 		if( restService != null )
272 		{
273 			return restService.getResourceByFullPath( getRequestStepConfig().getResourcePath() );
274 		}
275 		return null;
276 	}
277 
278 	private RestMethod findRestMethod()
279 	{
280 		if( !restRequestStepConfig.isSetMethodName() )
281 		{
282 			RestRequestConverter.updateRestTestRequest( this );
283 
284 			// Must be an old version RestRequest...
285 			if( getResource() == null )
286 			{
287 				restResource = RestRequestConverter.resolveResource( this );
288 				if( restResource == null )
289 					return null;
290 				getRequestStepConfig().setService( restResource.getInterface().getName() );
291 				getRequestStepConfig().setResourcePath( restResource.getFullPath() );
292 			}
293 			RestMethod method = RestRequestConverter.getMethod( getResource(), getRequestStepConfig().getRestRequest()
294 					.selectAttribute( null, "method" ).newCursor().getTextValue(), getRequestStepConfig().getRestRequest()
295 					.getName() );
296 			restRequestStepConfig.setMethodName( method.getName() );
297 			return method;
298 		}
299 		else if( getResource() == null )
300 		{
301 			restResource = RestRequestConverter.resolveResource( this );
302 			if( restResource == null )
303 				return null;
304 			getRequestStepConfig().setService( restResource.getInterface().getName() );
305 			getRequestStepConfig().setResourcePath( restResource.getFullPath() );
306 
307 			RestMethod m = ( RestMethod )getWsdlModelItemByName( getResource().getRestMethodList(), getRequestStepConfig()
308 					.getMethodName() );
309 			if( m == null )
310 			{
311 				String mn = null;
312 				while( mn == null )
313 				{
314 					mn = UISupport.prompt( "Select method in REST Resource [" + restResource.getName() + "]",
315 							"Missing REST Method", ModelSupport.getNames( restResource.getRestMethodList() ) );
316 				}
317 
318 				return restResource.getRestMethodByName( mn );
319 			}
320 		}
321 
322 		return ( RestMethod )getWsdlModelItemByName( getResource().getRestMethodList(), getRequestStepConfig()
323 				.getMethodName() );
324 	}
325 
326 	public RestMethod getRestMethod()
327 	{
328 		if( restMethod == null )
329 			restMethod = findRestMethod();
330 		return restMethod;
331 	}
332 
333 	public RestResource getResource()
334 	{
335 		if( restResource == null )
336 			restResource = findRestResource();
337 		return restResource;
338 	}
339 
340 	public Operation getOperation()
341 	{
342 		return getResource();
343 	}
344 
345 	@Override
346 	public void release()
347 	{
348 		super.release();
349 
350 		if( restResource != null )
351 		{
352 			restResource.removePropertyChangeListener( this );
353 			restResource.getService().getProject().removeProjectListener( projectListener );
354 			restResource.getService().removeInterfaceListener( interfaceListener );
355 			restResource.getService().removePropertyChangeListener( this );
356 		}
357 
358 		if( restMethod != null )
359 		{
360 			restMethod.removePropertyChangeListener( this );
361 		}
362 
363 		if( testRequest != null )
364 		{
365 			testRequest.removePropertyChangeListener( this );
366 			testRequest.release();
367 		}
368 	}
369 
370 	@Override
371 	public void resetConfigOnMove( TestStepConfig config )
372 	{
373 		super.resetConfigOnMove( config );
374 
375 		restRequestStepConfig = ( RestRequestStepConfig )config.getConfig().changeType( RestRequestStepConfig.type );
376 		testRequest.updateConfig( restRequestStepConfig.getRestRequest() );
377 	}
378 
379 	public void propertyChange( PropertyChangeEvent evt )
380 	{
381 		if( evt.getSource() == restResource )
382 		{
383 			if( evt.getPropertyName().equals( RestResource.PATH_PROPERTY ) )
384 			{
385 				getRequestStepConfig().setResourcePath( restResource.getFullPath() );
386 			}
387 			else if( evt.getPropertyName().equals( "childMethods" ) && restMethod == evt.getOldValue() )
388 			{
389 				// TODO: Convert to HttpTestRequestStep
390 				log.debug( "Removing test step due to removed Rest method" );
391 				getTestCase().removeTestStep( RestTestRequestStep.this );
392 			}
393 		}
394 		else if( restResource != null && evt.getSource() == restResource.getInterface() )
395 		{
396 			if( evt.getPropertyName().equals( Interface.NAME_PROPERTY ) )
397 			{
398 				getRequestStepConfig().setService( ( String )evt.getNewValue() );
399 			}
400 		}
401 		else if( evt.getSource() == restMethod )
402 		{
403 			if( evt.getPropertyName().equals( RestMethod.NAME_PROPERTY ) )
404 			{
405 				getRequestStepConfig().setMethodName( ( String )evt.getNewValue() );
406 			}
407 		}
408 		if( evt.getPropertyName().equals( TestAssertion.CONFIGURATION_PROPERTY )
409 				|| evt.getPropertyName().equals( TestAssertion.DISABLED_PROPERTY ) )
410 		{
411 			if( getTestRequest().getResponse() != null )
412 			{
413 				getTestRequest().assertResponse( new WsdlTestRunContext( this ) );
414 			}
415 		}
416 		else
417 		{
418 			if( evt.getSource() == testRequest && evt.getPropertyName().equals( WsdlTestRequest.NAME_PROPERTY ) )
419 			{
420 				if( !super.getName().equals( evt.getNewValue() ) )
421 					super.setName( ( String )evt.getNewValue() );
422 			}
423 			else if( evt.getSource() == testRequest && evt.getPropertyName().equals( "restMethod" ) )
424 			{
425 				refreshRequestProperties();
426 			}
427 
428 			notifyPropertyChanged( evt.getPropertyName(), evt.getOldValue(), evt.getNewValue() );
429 		}
430 
431 		// TODO copy from HttpTestRequestStep super.propertyChange( evt );
432 	}
433 
434 	public class InternalProjectListener extends ProjectListenerAdapter
435 	{
436 		@Override
437 		public void interfaceRemoved( Interface iface )
438 		{
439 			if( restResource != null && restResource.getInterface().equals( iface ) )
440 			{
441 				log.debug( "Removing test step due to removed interface" );
442 				( getTestCase() ).removeTestStep( RestTestRequestStep.this );
443 			}
444 		}
445 	}
446 
447 	public class InternalInterfaceListener extends InterfaceListenerAdapter
448 	{
449 		@Override
450 		public void operationRemoved( Operation operation )
451 		{
452 			if( operation == restResource )
453 			{
454 				log.debug( "Removing test step due to removed operation" );
455 				( getTestCase() ).removeTestStep( RestTestRequestStep.this );
456 			}
457 		}
458 
459 		@Override
460 		public void operationUpdated( Operation operation )
461 		{
462 			if( operation == restResource )
463 			{
464 				// requestStepConfig.setResourcePath( operation.get );
465 			}
466 		}
467 	}
468 
469 	@Override
470 	public boolean dependsOn( AbstractWsdlModelItem<?> modelItem )
471 	{
472 		if( modelItem instanceof Interface && getTestRequest().getOperation() != null
473 				&& getTestRequest().getOperation().getInterface() == modelItem )
474 		{
475 			return true;
476 		}
477 		else if( modelItem instanceof Operation && getTestRequest().getOperation() == modelItem )
478 		{
479 			return true;
480 		}
481 
482 		return false;
483 	}
484 
485 	public void setRestMethod( RestMethod method )
486 	{
487 		if( restMethod == method )
488 			return;
489 
490 		RestMethod oldMethod = restMethod;
491 		restMethod = method;
492 
493 		getRequestStepConfig().setService( method.getInterface().getName() );
494 		getRequestStepConfig().setResourcePath( method.getResource().getFullPath() );
495 		getRequestStepConfig().setMethodName( method.getName() );
496 
497 		// new resource?
498 		RestResource res = findRestResource();
499 		if( res != getResource() )
500 		{
501 			restResource.removePropertyChangeListener( this );
502 			restResource.getService().removeInterfaceListener( interfaceListener );
503 			restResource.getService().removePropertyChangeListener( this );
504 
505 			restResource = res;
506 
507 			restResource.getService().addInterfaceListener( interfaceListener );
508 			restResource.getService().addPropertyChangeListener( this );
509 			restResource.addPropertyChangeListener( this );
510 		}
511 
512 		if( oldMethod != null )
513 			oldMethod.removePropertyChangeListener( this );
514 
515 		restMethod.addPropertyChangeListener( this );
516 		getTestRequest().setRestMethod( restMethod );
517 	}
518 
519 	public RestTestRequest getTestRequest()
520 	{
521 		return testRequest;
522 	}
523 
524 	public Interface getInterface()
525 	{
526 		return getResource() == null ? null : getResource().getInterface();
527 	}
528 
529 	@Override
530 	public ImageIcon getIcon()
531 	{
532 		return testRequest == null ? null : testRequest.getIcon();
533 	}
534 
535 	public TestStep getTestStep()
536 	{
537 		return this;
538 	}
539 
540 	@Override
541 	@SuppressWarnings( "unchecked" )
542 	public void resolve( ResolveContext<?> context )
543 	{
544 		super.resolve( context );
545 
546 		if( getRestMethod() == null )
547 		{
548 			if( context.hasThisModelItem( this, "Missing REST Method in Project", getRequestStepConfig().getService()
549 					+ "/" + getRequestStepConfig().getMethodName() ) )
550 				return;
551 			context.addPathToResolve( this, "Missing REST Method in Project",
552 					getRequestStepConfig().getService() + "/" + getRequestStepConfig().getMethodName() ).addResolvers(
553 					new RemoveTestStepResolver( this ), new ImportInterfaceResolver( this )
554 					{
555 						@Override
556 						protected boolean update()
557 						{
558 							RestMethod restMethod = findRestMethod();
559 							if( restMethod == null )
560 								return false;
561 
562 							setRestMethod( restMethod );
563 							initRestTestRequest();
564 							setDisabled( false );
565 							return true;
566 						}
567 
568 					}, new ChangeRestMethodResolver( this )
569 					{
570 						@Override
571 						public boolean update()
572 						{
573 							RestMethod restMethod = getSelectedRestMethod();
574 							if( restMethod == null )
575 								return false;
576 
577 							setRestMethod( restMethod );
578 							initRestTestRequest();
579 							setDisabled( false );
580 							return true;
581 						}
582 
583 						@Override
584 						protected Interface[] getInterfaces( WsdlProject project )
585 						{
586 							List<RestService> interfaces = ModelSupport.getChildren( project, RestService.class );
587 							return interfaces.toArray( new Interface[interfaces.size()] );
588 						}
589 					} );
590 		}
591 		else
592 		{
593 			getRestMethod().resolve( context );
594 			if( context.hasThisModelItem( this, "Missing REST Method in Project", getRequestStepConfig().getService()
595 					+ "/" + getRequestStepConfig().getMethodName() ) )
596 			{
597 				PathToResolve path = context.getPath( this, "Missing REST Method in Project", getRequestStepConfig()
598 						.getService()
599 						+ "/" + getRequestStepConfig().getMethodName() );
600 				path.setSolved( true );
601 			}
602 		}
603 	}
604 
605 	@Override
606 	public void prepare( TestCaseRunner testRunner, TestCaseRunContext testRunContext ) throws Exception
607 	{
608 		super.prepare( testRunner, testRunContext );
609 
610 		testRequest.setResponse( null, testRunContext );
611 
612 		for( TestAssertion assertion : testRequest.getAssertionList() )
613 		{
614 			assertion.prepare( testRunner, testRunContext );
615 		}
616 	}
617 
618 	/*
619 	 * @SuppressWarnings("unchecked") public void resolve(ResolveContext<?>
620 	 * context) { super.resolve(context);
621 	 * 
622 	 * if (getResource() == null) { if (context.hasThisModelItem(this,
623 	 * "Missing REST Resource in Project", getRequestStepConfig() .getService() +
624 	 * "/" + getRequestStepConfig().getResourcePath())) return;
625 	 * context.addPathToResolve( this, "Missing REST Resource in Project",
626 	 * getRequestStepConfig().getService() + "/" +
627 	 * getRequestStepConfig().getResourcePath()) .addResolvers(new
628 	 * RemoveTestStepResolver(this), new ImportInterfaceResolver(this) {
629 	 * 
630 	 * @Override protected boolean update() { RestResource restResource =
631 	 * findRestResource(); if (restResource == null) return false;
632 	 * 
633 	 * setResource(restResource); initRestTestRequest(); setDisabled(false);
634 	 * return true; }
635 	 * 
636 	 * }, new ChangeOperationResolver(this, "Resource") {
637 	 * 
638 	 * @Override public boolean update() { RestResource restResource =
639 	 * (RestResource) getSelectedOperation(); if (restResource == null) return
640 	 * false;
641 	 * 
642 	 * setResource(restResource); initRestTestRequest(); setDisabled(false);
643 	 * return true; }
644 	 * 
645 	 * protected Interface[] getInterfaces( WsdlProject project) {
646 	 * List<RestService> interfaces = ModelSupport .getChildren(project,
647 	 * RestService.class); return interfaces .toArray(new Interface[interfaces
648 	 * .size()]); } }); } else { getResource().resolve(context); if
649 	 * (context.hasThisModelItem(this, "Missing REST Resource in Project",
650 	 * getRequestStepConfig() .getService() + "/" +
651 	 * getRequestStepConfig().getResourcePath())) { PathToResolve path =
652 	 * context.getPath(this, "Missing REST Resource in Project",
653 	 * getRequestStepConfig().getService() + "/" +
654 	 * getRequestStepConfig().getResourcePath()); path.setSolved(true); } } }
655 	 */
656 
657 	public PropertyExpansion[] getPropertyExpansions()
658 	{
659 		PropertyExpansionsResult result = new PropertyExpansionsResult( this, testRequest );
660 
661 		result.extractAndAddAll( "requestContent" );
662 		result.extractAndAddAll( "endpoint" );
663 		result.extractAndAddAll( "username" );
664 		result.extractAndAddAll( "password" );
665 		result.extractAndAddAll( "domain" );
666 
667 		StringToStringsMap requestHeaders = testRequest.getRequestHeaders();
668 		for( String key : requestHeaders.keySet() )
669 		{
670 			for( String value : requestHeaders.get( key ) )
671 				result.extractAndAddAll( new HttpTestRequestStep.RequestHeaderHolder( key, value, testRequest ), "value" );
672 		}
673 
674 		return result.toArray( new PropertyExpansion[result.size()] );
675 	}
676 
677 	public AbstractHttpRequest<?> getHttpRequest()
678 	{
679 		return testRequest;
680 	}
681 
682 	public TestAssertion addAssertion( String type )
683 	{
684 		WsdlMessageAssertion result = testRequest.addAssertion( type );
685 		return result;
686 	}
687 
688 	public void addAssertionsListener( AssertionsListener listener )
689 	{
690 		testRequest.addAssertionsListener( listener );
691 	}
692 
693 	public TestAssertion cloneAssertion( TestAssertion source, String name )
694 	{
695 		return testRequest.cloneAssertion( source, name );
696 	}
697 
698 	public String getAssertableContent()
699 	{
700 		return testRequest.getAssertableContent();
701 	}
702 
703 	public AssertableType getAssertableType()
704 	{
705 		return testRequest.getAssertableType();
706 	}
707 
708 	public TestAssertion getAssertionByName( String name )
709 	{
710 		return testRequest.getAssertionByName( name );
711 	}
712 
713 	public List<TestAssertion> getAssertionList()
714 	{
715 		return testRequest.getAssertionList();
716 	}
717 
718 	public AssertionStatus getAssertionStatus()
719 	{
720 		return testRequest.getAssertionStatus();
721 	}
722 
723 	public void removeAssertion( TestAssertion assertion )
724 	{
725 		testRequest.removeAssertion( assertion );
726 	}
727 
728 	public void removeAssertionsListener( AssertionsListener listener )
729 	{
730 		testRequest.removeAssertionsListener( listener );
731 	}
732 
733 	public TestAssertion moveAssertion( int ix, int offset )
734 	{
735 		return testRequest.moveAssertion( ix, offset );
736 	}
737 
738 	public Map<String, TestAssertion> getAssertions()
739 	{
740 		return testRequest.getAssertions();
741 	}
742 
743 	public WsdlMessageAssertion getAssertionAt( int index )
744 	{
745 		return testRequest.getAssertionAt( index );
746 	}
747 
748 	public int getAssertionCount()
749 	{
750 		return testRequest == null ? 0 : testRequest.getAssertionCount();
751 	}
752 
753 	public String getDefaultAssertableContent()
754 	{
755 		return testRequest.getDefaultAssertableContent();
756 	}
757 
758 	public TestStepResult run( TestCaseRunner runner, TestCaseRunContext runContext )
759 	{
760 		RestRequestStepResult testStepResult = new RestRequestStepResult( this );
761 
762 		try
763 		{
764 			submit = testRequest.submit( runContext, false );
765 			HttpResponse response = ( HttpResponse )submit.getResponse();
766 
767 			if( submit.getStatus() != Submit.Status.CANCELED )
768 			{
769 				if( submit.getStatus() == Submit.Status.ERROR )
770 				{
771 					testStepResult.setStatus( TestStepStatus.FAILED );
772 					testStepResult.addMessage( submit.getError().toString() );
773 
774 					testRequest.setResponse( null, runContext );
775 				}
776 				else if( response == null )
777 				{
778 					testStepResult.setStatus( TestStepStatus.FAILED );
779 					testStepResult.addMessage( "Request is missing response" );
780 
781 					testRequest.setResponse( null, runContext );
782 				}
783 				else
784 				{
785 					runContext.setProperty( AssertedXPathsContainer.ASSERTEDXPATHSCONTAINER_PROPERTY, testStepResult );
786 					testRequest.setResponse( response, runContext );
787 
788 					testStepResult.setTimeTaken( response.getTimeTaken() );
789 					testStepResult.setSize( response.getContentLength() );
790 					testStepResult.setResponse( response );
791 
792 					switch( testRequest.getAssertionStatus() )
793 					{
794 					case FAILED :
795 						testStepResult.setStatus( TestStepStatus.FAILED );
796 						break;
797 					case VALID :
798 						testStepResult.setStatus( TestStepStatus.OK );
799 						break;
800 					case UNKNOWN :
801 						testStepResult.setStatus( TestStepStatus.UNKNOWN );
802 						break;
803 					}
804 				}
805 			}
806 			else
807 			{
808 				testStepResult.setStatus( TestStepStatus.CANCELED );
809 				testStepResult.addMessage( "Request was canceled" );
810 			}
811 
812 			if( response != null )
813 			{
814 				testStepResult.setRequestContent( response.getRequestContent() );
815 				testStepResult.addProperty( "URL", response.getURL() == null ? "<missing>" : response.getURL().toString() );
816 				testStepResult.addProperty( "Method", String.valueOf( response.getMethod() ) );
817 				testStepResult.addProperty( "StatusCode", String.valueOf( response.getStatusCode() ) );
818 				testStepResult.addProperty( "HTTP Version", response.getHttpVersion() );
819 			}
820 			else
821 				testStepResult.setRequestContent( testRequest.getRequestContent() );
822 		}
823 		catch( SubmitException e )
824 		{
825 			testStepResult.setStatus( TestStepStatus.FAILED );
826 			testStepResult.addMessage( "SubmitException: " + e );
827 		}
828 		finally
829 		{
830 			submit = null;
831 		}
832 
833 		testStepResult.setDomain( PropertyExpander.expandProperties( runContext, testRequest.getDomain() ) );
834 		testStepResult.setUsername( PropertyExpander.expandProperties( runContext, testRequest.getUsername() ) );
835 		testStepResult.setEndpoint( PropertyExpander.expandProperties( runContext, testRequest.getEndpoint() ) );
836 		testStepResult.setPassword( PropertyExpander.expandProperties( runContext, testRequest.getPassword() ) );
837 		testStepResult.setEncoding( PropertyExpander.expandProperties( runContext, testRequest.getEncoding() ) );
838 
839 		if( testStepResult.getStatus() != TestStepStatus.CANCELED )
840 		{
841 			AssertionStatus assertionStatus = testRequest.getAssertionStatus();
842 			switch( assertionStatus )
843 			{
844 			case FAILED :
845 			{
846 				testStepResult.setStatus( TestStepStatus.FAILED );
847 				if( getAssertionCount() == 0 )
848 				{
849 					testStepResult.addMessage( "Invalid/empty response" );
850 				}
851 				else
852 					for( int c = 0; c < getAssertionCount(); c++ )
853 					{
854 						WsdlMessageAssertion assertion = getAssertionAt( c );
855 						AssertionError[] errors = assertion.getErrors();
856 						if( errors != null )
857 						{
858 							for( AssertionError error : errors )
859 							{
860 								testStepResult.addMessage( "[" + assertion.getName() + "] " + error.getMessage() );
861 							}
862 						}
863 					}
864 
865 				break;
866 			}
867 			}
868 		}
869 
870 		if( testRequest.isDiscardResponse() && !SoapUI.getDesktop().hasDesktopPanel( this ) )
871 			testRequest.setResponse( null, runContext );
872 
873 		return testStepResult;
874 	}
875 
876 	private class InternalTestPropertyListener extends TestPropertyListenerAdapter
877 	{
878 		@Override
879 		public void propertyAdded( String name )
880 		{
881 			requestProperties.put( name, new RestTestStepProperty( name ) );
882 			RestTestRequestStep.this.addProperty( requestProperties.get( name ), true );
883 		}
884 
885 		@Override
886 		public void propertyRemoved( String name )
887 		{
888 			requestProperties.remove( name );
889 			deleteProperty( name, true );
890 		}
891 
892 		@Override
893 		public void propertyRenamed( String oldName, String newName )
894 		{
895 			RestTestStepProperty prop = requestProperties.remove( oldName );
896 			if( prop != null )
897 			{
898 				prop.setPropertyName( newName );
899 				requestProperties.put( newName, prop );
900 			}
901 			RestTestRequestStep.this.propertyRenamed( oldName );
902 		}
903 
904 		@Override
905 		public void propertyValueChanged( String name, String oldValue, String newValue )
906 		{
907 			firePropertyValueChanged( name, oldValue, newValue );
908 		}
909 
910 		@Override
911 		public void propertyMoved( String name, int oldIndex, int newIndex )
912 		{
913 			firePropertyMoved( name, oldIndex, newIndex );
914 		}
915 	}
916 
917 	private class RestTestStepProperty implements TestStepProperty
918 	{
919 		private String propertyName;
920 
921 		public RestTestStepProperty( String propertyName )
922 		{
923 			this.propertyName = propertyName;
924 		}
925 
926 		public void setPropertyName( String name )
927 		{
928 			propertyName = name;
929 		}
930 
931 		public TestStep getTestStep()
932 		{
933 			return RestTestRequestStep.this;
934 		}
935 
936 		public String getName()
937 		{
938 			return propertyName;
939 		}
940 
941 		public String getDescription()
942 		{
943 			return getTestRequest().getProperty( propertyName ).getDescription();
944 		}
945 
946 		public String getValue()
947 		{
948 			return getTestRequest().getProperty( propertyName ).getValue();
949 		}
950 
951 		public String getDefaultValue()
952 		{
953 			return getTestRequest().getProperty( propertyName ).getDefaultValue();
954 		}
955 
956 		public void setValue( String value )
957 		{
958 			getTestRequest().getProperty( propertyName ).setValue( value );
959 		}
960 
961 		public boolean isReadOnly()
962 		{
963 			return false;
964 		}
965 
966 		public QName getType()
967 		{
968 			return getTestRequest().getProperty( propertyName ).getType();
969 		}
970 
971 		public ModelItem getModelItem()
972 		{
973 			return getTestRequest();
974 		}
975 	}
976 }