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