View Javadoc

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