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.loadtest;
14  
15  import java.beans.PropertyChangeEvent;
16  import java.beans.PropertyChangeListener;
17  import java.io.File;
18  import java.io.FileNotFoundException;
19  import java.io.PrintWriter;
20  import java.util.ArrayList;
21  import java.util.Date;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Set;
25  
26  import org.apache.commons.collections.list.TreeList;
27  import org.apache.log4j.Logger;
28  
29  import com.eviware.soapui.SoapUI;
30  import com.eviware.soapui.config.LoadStrategyConfig;
31  import com.eviware.soapui.config.LoadTestAssertionConfig;
32  import com.eviware.soapui.config.LoadTestConfig;
33  import com.eviware.soapui.config.LoadTestLimitTypesConfig;
34  import com.eviware.soapui.config.LoadTestLimitTypesConfig.Enum;
35  import com.eviware.soapui.impl.wsdl.AbstractWsdlModelItem;
36  import com.eviware.soapui.impl.wsdl.loadtest.assertions.AbstractLoadTestAssertion;
37  import com.eviware.soapui.impl.wsdl.loadtest.assertions.LoadTestAssertionRegistry;
38  import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics;
39  import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLog;
40  import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLogErrorEntry;
41  import com.eviware.soapui.impl.wsdl.loadtest.strategy.BurstLoadStrategy;
42  import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategy;
43  import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategyFactory;
44  import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategyRegistry;
45  import com.eviware.soapui.impl.wsdl.loadtest.strategy.SimpleLoadStrategy;
46  import com.eviware.soapui.impl.wsdl.support.Configurable;
47  import com.eviware.soapui.impl.wsdl.testcase.WsdlTestCase;
48  import com.eviware.soapui.impl.wsdl.testcase.WsdlTestCaseRunner;
49  import com.eviware.soapui.impl.wsdl.teststeps.SimplePathPropertySupport;
50  import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestStep;
51  import com.eviware.soapui.model.support.LoadTestRunListenerAdapter;
52  import com.eviware.soapui.model.testsuite.LoadTest;
53  import com.eviware.soapui.model.testsuite.LoadTestRunContext;
54  import com.eviware.soapui.model.testsuite.LoadTestRunListener;
55  import com.eviware.soapui.model.testsuite.LoadTestRunner;
56  import com.eviware.soapui.model.testsuite.TestCaseRunContext;
57  import com.eviware.soapui.model.testsuite.TestCaseRunner;
58  import com.eviware.soapui.model.testsuite.TestRunnable;
59  import com.eviware.soapui.model.testsuite.TestRunner;
60  import com.eviware.soapui.model.testsuite.TestStepResult;
61  import com.eviware.soapui.model.testsuite.TestRunner.Status;
62  import com.eviware.soapui.settings.HttpSettings;
63  import com.eviware.soapui.support.StringUtils;
64  import com.eviware.soapui.support.scripting.SoapUIScriptEngine;
65  import com.eviware.soapui.support.scripting.SoapUIScriptEngineRegistry;
66  import com.eviware.soapui.support.types.StringList;
67  import com.eviware.soapui.support.types.StringToObjectMap;
68  
69  /***
70   * TestCase implementation for LoadTests
71   * 
72   * @author Ole.Matzura
73   * @todo add assertionFailed event to LoadTestListener
74   * @todo create and return LoadTestAssertionResult from load-test assertions
75   */
76  
77  @SuppressWarnings( "unchecked" )
78  public class WsdlLoadTest extends AbstractWsdlModelItem<LoadTestConfig> implements LoadTest, TestRunnable
79  {
80  	public final static String THREADCOUNT_PROPERTY = WsdlLoadTest.class.getName() + "@threadcount";
81  	public final static String STARTDELAY_PROPERTY = WsdlLoadTest.class.getName() + "@startdelay";
82  	public final static String TESTLIMIT_PROPERTY = WsdlLoadTest.class.getName() + "@testlimit";
83  	public final static String HISTORYLIMIT_PROPERTY = WsdlLoadTest.class.getName() + "@historylimit";
84  	public final static String LIMITTYPE_PROPERRY = WsdlLoadTest.class.getName() + "@limittype";
85  	public final static String SAMPLEINTERVAL_PROPERRY = WsdlLoadTest.class.getName() + "@sample-interval";
86  	public static final String MAXASSERTIONERRORS_PROPERTY = WsdlLoadTest.class.getName() + "@max-assertion-errors";
87  	public final static String SETUP_SCRIPT_PROPERTY = WsdlTestCase.class.getName() + "@setupScript";
88  	public final static String TEARDOWN_SCRIPT_PROPERTY = WsdlTestCase.class.getName() + "@tearDownScript";
89  
90  	private final static Logger logger = Logger.getLogger( WsdlLoadTest.class );
91  	public static final int DEFAULT_STRATEGY_INTERVAL = 500;
92  
93  	private InternalTestRunListener internalTestRunListener = new InternalTestRunListener();
94  
95  	private WsdlTestCase testCase;
96  	private LoadTestStatistics statisticsModel;
97  	private LoadStrategy loadStrategy = new BurstLoadStrategy( this );
98  	private LoadTestLog loadTestLog;
99  
100 	private LoadStrategyConfigurationChangeListener loadStrategyListener = new LoadStrategyConfigurationChangeListener();
101 	private List<LoadTestAssertion> assertions = new ArrayList<LoadTestAssertion>();
102 	private ConfigurationChangePropertyListener configurationChangeListener = new ConfigurationChangePropertyListener();
103 	private Set<LoadTestListener> loadTestListeners = new HashSet<LoadTestListener>();
104 	private Set<LoadTestRunListener> loadTestRunListeners = new HashSet<LoadTestRunListener>();
105 	private List<LoadTestLogErrorEntry> assertionErrors = new TreeList();
106 	private WsdlLoadTestRunner runner;
107 	private StatisticsLogger statisticsLogger = new StatisticsLogger();
108 	private SoapUIScriptEngine setupScriptEngine;
109 	private SoapUIScriptEngine tearDownScriptEngine;
110 	@SuppressWarnings( "unused" )
111 	private SimplePathPropertySupport logFolder;
112 	private LoadTestRunListener[] loadTestRunListenersArray;
113 
114 	public WsdlLoadTest( WsdlTestCase testCase, LoadTestConfig config )
115 	{
116 		super( config, testCase, "/loadTest.gif" );
117 
118 		this.testCase = testCase;
119 
120 		if( getConfig().getThreadCount() < 1 )
121 			getConfig().setThreadCount( 5 );
122 
123 		if( getConfig().getLimitType() == null )
124 		{
125 			getConfig().setLimitType( LoadTestLimitTypesConfig.TIME );
126 			getConfig().setTestLimit( 60 );
127 		}
128 
129 		if( !getConfig().isSetHistoryLimit() )
130 		{
131 			getConfig().setHistoryLimit( -1 );
132 		}
133 
134 		addLoadTestRunListener( internalTestRunListener );
135 
136 		LoadStrategyConfig ls = getConfig().getLoadStrategy();
137 		if( ls == null )
138 		{
139 			ls = getConfig().addNewLoadStrategy();
140 			ls.setType( SimpleLoadStrategy.STRATEGY_TYPE );
141 		}
142 
143 		LoadStrategyFactory factory = LoadStrategyRegistry.getInstance().getFactory( ls.getType() );
144 		if( factory == null )
145 		{
146 			ls.setType( SimpleLoadStrategy.STRATEGY_TYPE );
147 			factory = LoadStrategyRegistry.getInstance().getFactory( ls.getType() );
148 		}
149 
150 		loadStrategy = factory.build( ls.getConfig(), this );
151 		loadStrategy.addConfigurationChangeListener( loadStrategyListener );
152 
153 		addLoadTestRunListener( loadStrategy );
154 
155 		statisticsModel = new LoadTestStatistics( this );
156 
157 		if( getConfig().xgetSampleInterval() == null )
158 			setSampleInterval( LoadTestStatistics.DEFAULT_SAMPLE_INTERVAL );
159 
160 		statisticsModel.setUpdateFrequency( getSampleInterval() );
161 
162 		List<LoadTestAssertionConfig> assertionList = getConfig().getAssertionList();
163 		for( LoadTestAssertionConfig assertionConfig : assertionList )
164 		{
165 			AbstractLoadTestAssertion assertion = LoadTestAssertionRegistry.buildAssertion( assertionConfig, this );
166 			if( assertion != null )
167 			{
168 				assertions.add( assertion );
169 				assertion.addPropertyChangeListener( LoadTestAssertion.CONFIGURATION_PROPERTY, configurationChangeListener );
170 			}
171 			else
172 			{
173 				logger.warn( "Failed to build LoadTestAssertion from getConfig() [" + assertionConfig + "]" );
174 			}
175 		}
176 
177 		if( getConfig().xgetResetStatisticsOnThreadCountChange() == null )
178 			getConfig().setResetStatisticsOnThreadCountChange( true );
179 
180 		if( getConfig().xgetCalculateTPSOnTimePassed() == null )
181 			getConfig().setCalculateTPSOnTimePassed( true );
182 
183 		if( !getConfig().isSetMaxAssertionErrors() )
184 			getConfig().setMaxAssertionErrors( 100 );
185 
186 		if( getConfig().xgetCancelExcessiveThreads() == null )
187 			getConfig().setCancelExcessiveThreads( true );
188 
189 		if( getConfig().xgetStrategyInterval() == null )
190 			getConfig().setStrategyInterval( DEFAULT_STRATEGY_INTERVAL );
191 
192 		loadTestLog = new LoadTestLog( this );
193 
194 		for( LoadTestRunListener listener : SoapUI.getListenerRegistry().getListeners( LoadTestRunListener.class ) )
195 		{
196 			addLoadTestRunListener( listener );
197 		}
198 
199 		// set close-connections to same as global so override works ok
200 		if( !getSettings().isSet( HttpSettings.CLOSE_CONNECTIONS ) )
201 			getSettings().setBoolean( HttpSettings.CLOSE_CONNECTIONS,
202 					SoapUI.getSettings().getBoolean( HttpSettings.CLOSE_CONNECTIONS ) );
203 	}
204 
205 	public LoadTestStatistics getStatisticsModel()
206 	{
207 		return statisticsModel;
208 	}
209 
210 	public StatisticsLogger getStatisticsLogger()
211 	{
212 		return statisticsLogger;
213 	}
214 
215 	public long getThreadCount()
216 	{
217 		return getConfig().getThreadCount();
218 	}
219 
220 	public void setThreadCount( long threadCount )
221 	{
222 		long oldCount = getThreadCount();
223 		if( threadCount == oldCount )
224 			return;
225 
226 		if( getLogStatisticsOnThreadChange() && isRunning() )
227 			statisticsLogger.logStatistics( "ThreadCount change from " + oldCount + " to " + threadCount );
228 
229 		getConfig().setThreadCount( ( int )threadCount );
230 		notifyPropertyChanged( THREADCOUNT_PROPERTY, oldCount, threadCount );
231 	}
232 
233 	public boolean getResetStatisticsOnThreadCountChange()
234 	{
235 		return getConfig().getResetStatisticsOnThreadCountChange();
236 	}
237 
238 	public void setResetStatisticsOnThreadCountChange( boolean value )
239 	{
240 		getConfig().setResetStatisticsOnThreadCountChange( value );
241 	}
242 
243 	public boolean getCancelOnReachedLimit()
244 	{
245 		return getConfig().getCancelOnReachedLimit();
246 	}
247 
248 	public void setCancelOnReachedLimit( boolean value )
249 	{
250 		getConfig().setCancelOnReachedLimit( value );
251 	}
252 
253 	public boolean getCancelExcessiveThreads()
254 	{
255 		return getConfig().getCancelExcessiveThreads();
256 	}
257 
258 	public void setCancelExcessiveThreads( boolean value )
259 	{
260 		getConfig().setCancelExcessiveThreads( value );
261 	}
262 
263 	public boolean getLogStatisticsOnThreadChange()
264 	{
265 		return getConfig().getLogStatisticsOnThreadChange();
266 	}
267 
268 	public void setLogStatisticsOnThreadChange( boolean value )
269 	{
270 		getConfig().setLogStatisticsOnThreadChange( value );
271 	}
272 
273 	public String getStatisticsLogFolder()
274 	{
275 		return getConfig().getStatisticsLogFolder();
276 	}
277 
278 	public void setStatisticsLogFolder( String value )
279 	{
280 		getConfig().setStatisticsLogFolder( value );
281 	}
282 
283 	public boolean getCalculateTPSOnTimePassed()
284 	{
285 		return getConfig().getCalculateTPSOnTimePassed();
286 	}
287 
288 	public void setCalculateTPSOnTimePassed( boolean value )
289 	{
290 		getConfig().setCalculateTPSOnTimePassed( value );
291 	}
292 
293 	public int getStartDelay()
294 	{
295 		return getConfig().getStartDelay();
296 	}
297 
298 	public void setStartDelay( int startDelay )
299 	{
300 		if( startDelay < 0 )
301 			return;
302 
303 		int oldDelay = getStartDelay();
304 		getConfig().setStartDelay( startDelay );
305 		notifyPropertyChanged( STARTDELAY_PROPERTY, oldDelay, startDelay );
306 	}
307 
308 	public long getHistoryLimit()
309 	{
310 		return getConfig().getHistoryLimit();
311 	}
312 
313 	public void setHistoryLimit( long historyLimit )
314 	{
315 		long oldLimit = getHistoryLimit();
316 		getConfig().setHistoryLimit( historyLimit );
317 		if( historyLimit == 0 )
318 
319 			notifyPropertyChanged( HISTORYLIMIT_PROPERTY, oldLimit, historyLimit );
320 	}
321 
322 	public long getTestLimit()
323 	{
324 		return getConfig().getTestLimit();
325 	}
326 
327 	public void setTestLimit( long testLimit )
328 	{
329 		if( testLimit < 0 )
330 			return;
331 
332 		long oldLimit = getTestLimit();
333 		getConfig().setTestLimit( testLimit );
334 		notifyPropertyChanged( TESTLIMIT_PROPERTY, oldLimit, testLimit );
335 	}
336 
337 	public long getMaxAssertionErrors()
338 	{
339 		return getConfig().getMaxAssertionErrors();
340 	}
341 
342 	public void setMaxAssertionErrors( long testLimit )
343 	{
344 		if( testLimit < 0 )
345 			return;
346 
347 		long oldLimit = getMaxAssertionErrors();
348 		getConfig().setMaxAssertionErrors( testLimit );
349 		notifyPropertyChanged( MAXASSERTIONERRORS_PROPERTY, oldLimit, testLimit );
350 	}
351 
352 	public long getStatisticsLogInterval()
353 	{
354 		return getConfig().getStatisticsLogInterval();
355 	}
356 
357 	public void setStatisticsLogInterval( int sampleInterval )
358 	{
359 		if( sampleInterval < 0 )
360 			return;
361 
362 		long oldInterval = getStatisticsLogInterval();
363 		getConfig().setStatisticsLogInterval( sampleInterval );
364 
365 		notifyPropertyChanged( SAMPLEINTERVAL_PROPERRY, oldInterval, sampleInterval );
366 
367 		if( oldInterval == 0 && sampleInterval > 0 && isRunning() )
368 			statisticsLogger.start();
369 	}
370 
371 	public long getSampleInterval()
372 	{
373 		return getConfig().getSampleInterval();
374 	}
375 
376 	public void setSampleInterval( int sampleInterval )
377 	{
378 		if( sampleInterval < 0 )
379 			return;
380 
381 		long oldInterval = getSampleInterval();
382 		getConfig().setSampleInterval( sampleInterval );
383 
384 		statisticsModel.setUpdateFrequency( sampleInterval );
385 		notifyPropertyChanged( SAMPLEINTERVAL_PROPERRY, oldInterval, sampleInterval );
386 	}
387 
388 	public Enum getLimitType()
389 	{
390 		return getConfig().getLimitType();
391 	}
392 
393 	public void setLimitType( Enum limitType )
394 	{
395 		if( limitType == null )
396 			return;
397 
398 		Enum oldType = getLimitType();
399 		getConfig().setLimitType( limitType );
400 		notifyPropertyChanged( LIMITTYPE_PROPERRY, oldType, limitType );
401 	}
402 
403 	public WsdlTestCase getTestCase()
404 	{
405 		return testCase;
406 	}
407 
408 	public synchronized WsdlLoadTestRunner run()
409 	{
410 		getStatisticsModel().reset();
411 		if( runner != null && runner.getStatus() == Status.RUNNING )
412 			return null;
413 
414 		if( runner != null )
415 			runner.release();
416 
417 		assertionErrors.clear();
418 		runner = new WsdlLoadTestRunner( this );
419 		runner.start();
420 		return runner;
421 	}
422 
423 	private class InternalTestRunListener extends LoadTestRunListenerAdapter
424 	{
425 		@Override
426 		public void afterLoadTest( LoadTestRunner loadTestRunner, LoadTestRunContext context )
427 		{
428 			statisticsLogger.finish();
429 		}
430 
431 		@Override
432 		public void beforeLoadTest( LoadTestRunner loadTestRunner, LoadTestRunContext context )
433 		{
434 			statisticsLogger.init( context );
435 
436 			if( getStatisticsLogInterval() > 0 )
437 				statisticsLogger.start();
438 		}
439 
440 		@Override
441 		public void afterTestCase( LoadTestRunner loadTestRunner, LoadTestRunContext context, TestCaseRunner testRunner,
442 				TestCaseRunContext runContext )
443 		{
444 			if( !assertions.isEmpty() )
445 			{
446 				for( LoadTestAssertion assertion : assertions )
447 				{
448 					String error = assertion.assertResults( loadTestRunner, context, testRunner, runContext );
449 					if( error != null )
450 					{
451 						int threadIndex = 0;
452 
453 						try
454 						{
455 							threadIndex = Integer.parseInt( runContext.getProperty( "ThreadIndex" ).toString() );
456 						}
457 						catch( Throwable t )
458 						{
459 						}
460 
461 						loadTestLog.addEntry( new LoadTestLogErrorEntry( assertion.getName(), error, assertion.getIcon(),
462 								threadIndex ) );
463 						statisticsModel.addError( LoadTestStatistics.TOTAL );
464 					}
465 				}
466 			}
467 		}
468 
469 		@Override
470 		public void afterTestStep( LoadTestRunner loadTestRunner, LoadTestRunContext context, TestCaseRunner testRunner,
471 				TestCaseRunContext runContext, TestStepResult result )
472 		{
473 			boolean added = false;
474 
475 			if( !assertions.isEmpty() )
476 			{
477 				for( LoadTestAssertion assertion : assertions )
478 				{
479 					String error = assertion.assertResult( loadTestRunner, context, result, testRunner, runContext );
480 					if( error != null )
481 					{
482 						int indexOfTestStep = testRunner.getTestCase().getIndexOfTestStep( result.getTestStep() );
483 						int threadIndex = 0;
484 
485 						try
486 						{
487 							threadIndex = Integer.parseInt( runContext.getProperty( "ThreadIndex" ).toString() );
488 						}
489 						catch( Throwable t )
490 						{
491 						}
492 
493 						LoadTestLogErrorEntry errorEntry = new LoadTestLogErrorEntry( assertion.getName(), error, result,
494 								assertion.getIcon(), threadIndex );
495 
496 						loadTestLog.addEntry( errorEntry );
497 						statisticsModel.addError( indexOfTestStep );
498 
499 						long maxAssertionErrors = getMaxAssertionErrors();
500 						if( maxAssertionErrors > 0 )
501 						{
502 							synchronized( assertionErrors )
503 							{
504 								assertionErrors.add( errorEntry );
505 								while( assertionErrors.size() > maxAssertionErrors )
506 								{
507 									assertionErrors.remove( 0 ).discard();
508 								}
509 							}
510 						}
511 
512 						added = true;
513 					}
514 				}
515 			}
516 
517 			// discard if set to discard and there were no errors
518 			if( !added )
519 			{
520 				if( getTestCase().getDiscardOkResults() || getTestCase().getMaxResults() == 0 )
521 				{
522 					result.discard();
523 				}
524 				else if( getTestCase().getMaxResults() > 0 && testRunner instanceof WsdlTestCaseRunner )
525 				{
526 					( ( WsdlTestCaseRunner )testRunner ).enforceMaxResults( getTestCase().getMaxResults() );
527 				}
528 			}
529 		}
530 	}
531 
532 	public LoadStrategy getLoadStrategy()
533 	{
534 		return loadStrategy;
535 	}
536 
537 	public void setLoadStrategy( LoadStrategy loadStrategy )
538 	{
539 		this.loadStrategy.removeConfigurationChangeListener( loadStrategyListener );
540 		removeLoadTestRunListener( this.loadStrategy );
541 
542 		this.loadStrategy = loadStrategy;
543 		this.loadStrategy.addConfigurationChangeListener( loadStrategyListener );
544 		addLoadTestRunListener( this.loadStrategy );
545 
546 		getConfig().getLoadStrategy().setType( loadStrategy.getType() );
547 		getConfig().getLoadStrategy().setConfig( loadStrategy.getConfig() );
548 	}
549 
550 	private class LoadStrategyConfigurationChangeListener implements PropertyChangeListener
551 	{
552 		public void propertyChange( PropertyChangeEvent evt )
553 		{
554 			getConfig().getLoadStrategy().setConfig( loadStrategy.getConfig() );
555 		}
556 	}
557 
558 	public LoadTestAssertion addAssertion( String type, String targetStep, boolean showConfig )
559 	{
560 		LoadTestAssertion assertion = LoadTestAssertionRegistry.createAssertion( type, this );
561 		assertion.setTargetStep( targetStep );
562 
563 		if( assertion instanceof Configurable && showConfig )
564 		{
565 			if( !( ( Configurable )assertion ).configure() )
566 				return null;
567 		}
568 
569 		assertions.add( assertion );
570 
571 		getConfig().addNewAssertion().set( assertion.getConfiguration() );
572 		assertion.addPropertyChangeListener( LoadTestAssertion.CONFIGURATION_PROPERTY, configurationChangeListener );
573 		fireAssertionAdded( assertion );
574 
575 		return assertion;
576 	}
577 
578 	public void removeAssertion( LoadTestAssertion assertion )
579 	{
580 		int ix = assertions.indexOf( assertion );
581 		if( ix >= 0 )
582 		{
583 			try
584 			{
585 				assertions.remove( ix );
586 				fireAssertionRemoved( assertion );
587 			}
588 			finally
589 			{
590 				assertion.removePropertyChangeListener( configurationChangeListener );
591 				assertion.release();
592 				getConfig().removeAssertion( ix );
593 			}
594 		}
595 	}
596 
597 	private void fireAssertionRemoved( LoadTestAssertion assertion )
598 	{
599 		if( !loadTestListeners.isEmpty() )
600 		{
601 			LoadTestListener[] l = loadTestListeners.toArray( new LoadTestListener[loadTestListeners.size()] );
602 			for( LoadTestListener listener : l )
603 			{
604 				listener.assertionRemoved( assertion );
605 			}
606 		}
607 	}
608 
609 	private void fireAssertionAdded( LoadTestAssertion assertion )
610 	{
611 		if( !loadTestListeners.isEmpty() )
612 		{
613 			LoadTestListener[] l = loadTestListeners.toArray( new LoadTestListener[loadTestListeners.size()] );
614 			for( LoadTestListener listener : l )
615 			{
616 				listener.assertionAdded( assertion );
617 			}
618 		}
619 	}
620 
621 	public int getAssertionCount()
622 	{
623 		return assertions.size();
624 	}
625 
626 	public LoadTestAssertion getAssertionAt( int index )
627 	{
628 		return index < 0 || index >= assertions.size() ? null : assertions.get( index );
629 	}
630 
631 	public LoadTestAssertion getAssertionByName( String name )
632 	{
633 		for( LoadTestAssertion assertion : assertions )
634 		{
635 			if( assertion.getName().equals( name ) )
636 			{
637 				return assertion;
638 			}
639 		}
640 		return null;
641 	}
642 
643 	private class ConfigurationChangePropertyListener implements PropertyChangeListener
644 	{
645 		public void propertyChange( PropertyChangeEvent evt )
646 		{
647 			int ix = assertions.indexOf( evt.getSource() );
648 			if( ix >= 0 )
649 			{
650 				getConfig().getAssertionArray( ix ).set( assertions.get( ix ).getConfiguration() );
651 			}
652 		}
653 	}
654 
655 	public LoadTestLog getLoadTestLog()
656 	{
657 		return loadTestLog;
658 	}
659 
660 	public List<LoadTestAssertion> getAssertionList()
661 	{
662 		return assertions;
663 	}
664 
665 	public void addLoadTestListener( LoadTestListener listener )
666 	{
667 		loadTestListeners.add( listener );
668 	}
669 
670 	public void removeLoadTestListener( LoadTestListener listener )
671 	{
672 		loadTestListeners.remove( listener );
673 	}
674 
675 	public void addLoadTestRunListener( LoadTestRunListener listener )
676 	{
677 		loadTestRunListeners.add( listener );
678 		loadTestRunListenersArray = null;
679 	}
680 
681 	public void removeLoadTestRunListener( LoadTestRunListener listener )
682 	{
683 		loadTestRunListeners.remove( listener );
684 		loadTestRunListenersArray = null;
685 	}
686 
687 	public LoadTestRunListener[] getLoadTestRunListeners()
688 	{
689 		if( loadTestRunListenersArray == null )
690 		{
691 			loadTestRunListenersArray = loadTestRunListeners
692 					.toArray( new LoadTestRunListener[loadTestRunListeners.size()] );
693 		}
694 
695 		return loadTestRunListenersArray;
696 	}
697 
698 	/***
699 	 * Release internal objects so they can remove listeners
700 	 */
701 
702 	@Override
703 	public void release()
704 	{
705 		super.release();
706 
707 		statisticsModel.release();
708 		loadTestLog.release();
709 
710 		for( LoadTestAssertion assertion : assertions )
711 			assertion.release();
712 
713 		loadTestRunListeners.clear();
714 		loadTestListeners.clear();
715 	}
716 
717 	public boolean isRunning()
718 	{
719 		return runner != null && runner.getStatus() == LoadTestRunner.Status.RUNNING;
720 	}
721 
722 	public WsdlLoadTestRunner getRunner()
723 	{
724 		return runner;
725 	}
726 
727 	public void resetConfigOnMove( LoadTestConfig config )
728 	{
729 		setConfig( config );
730 
731 		loadStrategy.updateConfig( config.getLoadStrategy().getConfig() );
732 
733 		List<LoadTestAssertionConfig> assertionList = config.getAssertionList();
734 		for( int c = 0; c < assertionList.size(); c++ )
735 		{
736 			assertions.get( c ).updateConfiguration( assertionList.get( c ) );
737 		}
738 	}
739 
740 	public class StatisticsLogger implements Runnable
741 	{
742 		private boolean stopped;
743 		private List<PrintWriter> writers = new ArrayList<PrintWriter>();
744 		private long startTime;
745 
746 		public void run()
747 		{
748 			stopped = false;
749 
750 			while( !stopped && getStatisticsLogInterval() > 0 )
751 			{
752 				try
753 				{
754 					long statisticsInterval = getStatisticsLogInterval();
755 					Thread.sleep( statisticsInterval );
756 					if( !stopped )
757 					{
758 						logStatistics( "Interval" );
759 					}
760 				}
761 				catch( InterruptedException e )
762 				{
763 					e.printStackTrace();
764 				}
765 			}
766 		}
767 
768 		public void start()
769 		{
770 			new Thread( this, "Statistics Logger for LoadTest [" + getName() + "]" ).start();
771 		}
772 
773 		public void init( LoadTestRunContext context )
774 		{
775 			writers.clear();
776 
777 			String statisticsLogFolder = context.expand( getStatisticsLogFolder() );
778 			if( StringUtils.isNullOrEmpty( statisticsLogFolder ) )
779 				return;
780 
781 			File folder = new File( statisticsLogFolder );
782 			if( !folder.exists() )
783 			{
784 				if( !folder.mkdirs() )
785 				{
786 					SoapUI
787 							.logError( new Exception( "Failed to create statistics log folder [" + statisticsLogFolder + "]" ) );
788 					return;
789 				}
790 			}
791 
792 			for( int c = 0; c < testCase.getTestStepCount(); c++ )
793 			{
794 				try
795 				{
796 					WsdlTestStep testStep = testCase.getTestStepAt( c );
797 					String fileName = StringUtils.createFileName( testStep.getName(), '_' ) + ".log";
798 					PrintWriter writer = new PrintWriter( new File( folder, fileName ) );
799 					writers.add( writer );
800 					addHeaders( writer );
801 				}
802 				catch( FileNotFoundException e )
803 				{
804 					e.printStackTrace();
805 					writers.add( null );
806 				}
807 			}
808 
809 			// and one writer for the testcase..
810 			try
811 			{
812 				String fileName = StringUtils.createFileName( testCase.getName(), '_' ) + ".log";
813 				writers.add( new PrintWriter( new File( folder, fileName ) ) );
814 			}
815 			catch( FileNotFoundException e )
816 			{
817 				e.printStackTrace();
818 			}
819 
820 			startTime = System.nanoTime();
821 		}
822 
823 		private void addHeaders( PrintWriter writer )
824 		{
825 			writer.print( "date,threads,elapsed,min,max,avg,last,cnt,tps,bytes,bps,err,reason\n" );
826 		}
827 
828 		public void finish()
829 		{
830 			stopped = true;
831 
832 			logStatistics( "Finished" );
833 			for( PrintWriter writer : writers )
834 			{
835 				if( writer != null )
836 					writer.close();
837 			}
838 		}
839 
840 		private synchronized void logStatistics( String trigger )
841 		{
842 			if( writers.isEmpty() )
843 				return;
844 
845 			long timestamp = System.nanoTime();
846 			String elapsedString = String.valueOf( ( timestamp - startTime ) / 1000000 );
847 			String dateString = new Date().toString();
848 			String threadCountString = String.valueOf( getThreadCount() );
849 
850 			StringList[] snapshot = statisticsModel.getSnapshot();
851 			for( int c = 0; c < snapshot.length; c++ )
852 			{
853 				PrintWriter writer = writers.get( c );
854 				if( writer == null )
855 					continue;
856 
857 				StringList values = snapshot[c];
858 				writer.append( dateString ).append( ',' );
859 				writer.append( threadCountString ).append( ',' );
860 				writer.append( elapsedString );
861 
862 				for( String value : values )
863 				{
864 					writer.append( ',' ).append( value );
865 				}
866 
867 				writer.append( ',' ).append( trigger ).append( '\n' );
868 				writer.flush();
869 			}
870 		}
871 	}
872 
873 	public void setSetupScript( String script )
874 	{
875 		String oldScript = getSetupScript();
876 
877 		if( !getConfig().isSetSetupScript() )
878 			getConfig().addNewSetupScript();
879 
880 		getConfig().getSetupScript().setStringValue( script );
881 		if( setupScriptEngine != null )
882 			setupScriptEngine.setScript( script );
883 
884 		notifyPropertyChanged( SETUP_SCRIPT_PROPERTY, oldScript, script );
885 	}
886 
887 	public String getSetupScript()
888 	{
889 		return getConfig().isSetSetupScript() ? getConfig().getSetupScript().getStringValue() : null;
890 	}
891 
892 	public void setTearDownScript( String script )
893 	{
894 		String oldScript = getTearDownScript();
895 
896 		if( !getConfig().isSetTearDownScript() )
897 			getConfig().addNewTearDownScript();
898 
899 		getConfig().getTearDownScript().setStringValue( script );
900 		if( tearDownScriptEngine != null )
901 			tearDownScriptEngine.setScript( script );
902 
903 		notifyPropertyChanged( TEARDOWN_SCRIPT_PROPERTY, oldScript, script );
904 	}
905 
906 	public String getTearDownScript()
907 	{
908 		return getConfig().isSetTearDownScript() ? getConfig().getTearDownScript().getStringValue() : null;
909 	}
910 
911 	public Object runSetupScript( LoadTestRunContext runContext, LoadTestRunner runner ) throws Exception
912 	{
913 		String script = getSetupScript();
914 		if( StringUtils.isNullOrEmpty( script ) )
915 			return null;
916 
917 		if( setupScriptEngine == null )
918 		{
919 			setupScriptEngine = SoapUIScriptEngineRegistry.create( this );
920 			setupScriptEngine.setScript( script );
921 		}
922 
923 		setupScriptEngine.setVariable( "context", runContext );
924 		setupScriptEngine.setVariable( "loadTestRunner", runner );
925 		setupScriptEngine.setVariable( "log", SoapUI.ensureGroovyLog() );
926 		return setupScriptEngine.run();
927 	}
928 
929 	public Object runTearDownScript( LoadTestRunContext runContext, LoadTestRunner runner ) throws Exception
930 	{
931 		String script = getTearDownScript();
932 		if( StringUtils.isNullOrEmpty( script ) )
933 			return null;
934 
935 		if( tearDownScriptEngine == null )
936 		{
937 			tearDownScriptEngine = SoapUIScriptEngineRegistry.create( this );
938 			tearDownScriptEngine.setScript( script );
939 		}
940 
941 		tearDownScriptEngine.setVariable( "context", runContext );
942 		tearDownScriptEngine.setVariable( "loadTestRunner", runner );
943 		tearDownScriptEngine.setVariable( "log", SoapUI.ensureGroovyLog() );
944 		return tearDownScriptEngine.run();
945 	}
946 
947 	public int getStrategyInterval()
948 	{
949 		return getConfig().getStrategyInterval();
950 	}
951 
952 	public void setStrategyInterval( int interval )
953 	{
954 		getConfig().setStrategyInterval( interval );
955 	}
956 
957 	public boolean getUpdateStatisticsPerTestStep()
958 	{
959 		return getConfig().getUpdateStatisticsPerTestStep();
960 	}
961 
962 	public void setUpdateStatisticsPerTestStep( boolean updateStatisticsPerTestStep )
963 	{
964 		getConfig().setUpdateStatisticsPerTestStep( updateStatisticsPerTestStep );
965 	}
966 
967 	public TestRunner run( StringToObjectMap context, boolean async )
968 	{
969 		// TODO Auto-generated method stub
970 		return null;
971 	}
972 }