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 loadTestLog.addEntry( new LoadTestLogErrorEntry( assertion.getName(), error, assertion.getIcon() ));
415 statisticsModel.addError( LoadTestStatistics.TOTAL );
416 }
417 }
418 }
419 }
420
421 @Override
422 public void afterTestStep(LoadTestRunner loadTestRunner, LoadTestRunContext context, TestRunner testRunner, TestRunContext runContext, TestStepResult result)
423 {
424 if( !assertions.isEmpty() )
425 {
426 boolean added = false;
427
428 for( LoadTestAssertion assertion : assertions )
429 {
430 String error = assertion.assertResult( loadTestRunner, context, result, testRunner, runContext );
431 if( error != null )
432 {
433 int indexOfTestStep = testRunner.getTestCase().getIndexOfTestStep( result.getTestStep() );
434
435 LoadTestLogErrorEntry errorEntry = new LoadTestLogErrorEntry( assertion.getName(), error, result, assertion.getIcon() );
436 loadTestLog.addEntry( errorEntry);
437 statisticsModel.addError( indexOfTestStep );
438
439 long maxAssertionErrors = getMaxAssertionErrors();
440 if( maxAssertionErrors > 0 )
441 {
442 synchronized( assertionErrors )
443 {
444 assertionErrors.add( errorEntry );
445 while( assertionErrors.size() > maxAssertionErrors )
446 {
447 assertionErrors.remove( 0 ).discard();
448 }
449 }
450 }
451
452 added = true;
453 }
454 }
455
456
457 if( !added )
458 {
459 result.discard();
460 }
461 }
462 else result.discard();
463 }
464 }
465
466 public LoadStrategy getLoadStrategy()
467 {
468 return loadStrategy;
469 }
470
471 public void setLoadStrategy( LoadStrategy loadStrategy )
472 {
473 this.loadStrategy.removeConfigurationChangeListener( loadStrategyListener );
474 removeLoadTestRunListener( this.loadStrategy );
475
476 this.loadStrategy = loadStrategy;
477 this.loadStrategy.addConfigurationChangeListener( loadStrategyListener );
478 addLoadTestRunListener( this.loadStrategy );
479
480 getConfig().getLoadStrategy().setType( loadStrategy.getType() );
481 getConfig().getLoadStrategy().setConfig( loadStrategy.getConfig() );
482 }
483
484 private class LoadStrategyConfigurationChangeListener implements PropertyChangeListener
485 {
486 public void propertyChange(PropertyChangeEvent evt)
487 {
488 getConfig().getLoadStrategy().setConfig( loadStrategy.getConfig() );
489 }
490 }
491
492 public LoadTestAssertion addAssertion( String type, String targetStep, boolean showConfig )
493 {
494 LoadTestAssertion assertion = LoadTestAssertionRegistry.createAssertion( type, this );
495 assertion.setTargetStep( targetStep );
496
497 if( assertion instanceof Configurable && showConfig )
498 {
499 if( !((Configurable)assertion).configure() )
500 return null;
501 }
502
503 assertions.add( assertion );
504
505 getConfig().addNewAssertion().set( assertion.getConfiguration() );
506 assertion.addPropertyChangeListener( LoadTestAssertion.CONFIGURATION_PROPERTY, configurationChangeListener );
507 fireAssertionAdded( assertion );
508
509 return assertion;
510 }
511
512 public void removeAssertion( LoadTestAssertion assertion)
513 {
514 int ix = assertions.indexOf( assertion );
515 if( ix >= 0 )
516 {
517 try
518 {
519 assertions.remove( ix );
520 fireAssertionRemoved(assertion);
521 }
522 finally
523 {
524 assertion.removePropertyChangeListener( configurationChangeListener );
525 assertion.release();
526 getConfig().removeAssertion( ix );
527 }
528 }
529 }
530
531 private void fireAssertionRemoved(LoadTestAssertion assertion)
532 {
533 if( !loadTestListeners.isEmpty() )
534 {
535 LoadTestListener[] l = loadTestListeners.toArray( new LoadTestListener[loadTestListeners.size()] );
536 for( LoadTestListener listener : l )
537 {
538 listener.assertionRemoved( assertion );
539 }
540 }
541 }
542
543 private void fireAssertionAdded(LoadTestAssertion assertion)
544 {
545 if( !loadTestListeners.isEmpty() )
546 {
547 LoadTestListener[] l = loadTestListeners.toArray( new LoadTestListener[loadTestListeners.size()] );
548 for( LoadTestListener listener : l )
549 {
550 listener.assertionAdded( assertion );
551 }
552 }
553 }
554
555 public int getAssertionCount()
556 {
557 return assertions.size();
558 }
559
560 public LoadTestAssertion getAssertionAt( int index )
561 {
562 return index < 0 || index >= assertions.size() ? null : assertions.get( index );
563 }
564
565 private class ConfigurationChangePropertyListener implements PropertyChangeListener
566 {
567 public void propertyChange(PropertyChangeEvent evt)
568 {
569 int ix = assertions.indexOf( evt.getSource() );
570 if( ix >= 0 )
571 {
572 getConfig().getAssertionArray( ix ).set( assertions.get( ix ).getConfiguration() );
573 }
574 }
575 }
576
577 public LoadTestLog getLoadTestLog()
578 {
579 return loadTestLog;
580 }
581
582 public List<LoadTestAssertion> getAssertionList()
583 {
584 return assertions;
585 }
586
587 public void addLoadTestListener( LoadTestListener listener )
588 {
589 loadTestListeners.add( listener );
590 }
591
592 public void removeLoadTestListener( LoadTestListener listener )
593 {
594 loadTestListeners.remove( listener );
595 }
596
597 public void addLoadTestRunListener(LoadTestRunListener listener)
598 {
599 loadTestRunListeners.add( listener );
600 }
601
602 public void removeLoadTestRunListener(LoadTestRunListener listener)
603 {
604 loadTestRunListeners.remove( listener );
605 }
606
607 public LoadTestRunListener [] getLoadTestRunListeners()
608 {
609 return loadTestRunListeners.toArray( new LoadTestRunListener[loadTestRunListeners.size()] );
610 }
611
612 /***
613 * Release internal objects so they can remove listeners
614 */
615
616 @Override
617 public void release()
618 {
619 super.release();
620
621 statisticsModel.release();
622 loadTestLog.release();
623
624 for( LoadTestAssertion assertion : assertions )
625 assertion.release();
626
627 loadTestRunListeners.clear();
628 loadTestListeners.clear();
629 }
630
631 public boolean isRunning()
632 {
633 return runner != null && runner.getStatus() == LoadTestRunner.Status.RUNNING;
634 }
635
636 public WsdlLoadTestRunner getRunner()
637 {
638 return runner;
639 }
640
641 public void resetConfigOnMove( LoadTestConfig config )
642 {
643 setConfig( config );
644
645 loadStrategy.updateConfig( config.getLoadStrategy().getConfig() );
646
647 List<LoadTestAssertionConfig> assertionList = config.getAssertionList();
648 for( int c = 0; c < assertionList.size(); c++ )
649 {
650 assertions.get( c ).updateConfiguration( assertionList.get( c ) );
651 }
652 }
653
654 @SuppressWarnings("unchecked")
655 public List<? extends ModelItem> getChildren()
656 {
657 return Collections.EMPTY_LIST;
658 }
659
660 public class StatisticsLogger implements Runnable
661 {
662 private boolean stopped;
663 private List<PrintWriter> writers = new ArrayList<PrintWriter>();
664 private long startTime;
665
666 public void run()
667 {
668 stopped = false;
669
670 while( !stopped && getStatisticsLogInterval() > 0 )
671 {
672 try
673 {
674 long statisticsInterval = getStatisticsLogInterval();
675 Thread.sleep( statisticsInterval );
676 if( !stopped )
677 {
678 logStatistics( "Interval" );
679 }
680 }
681 catch( InterruptedException e )
682 {
683 e.printStackTrace();
684 }
685 }
686 }
687
688 public void start()
689 {
690 new Thread( this, "Statistics Logger for LoadTest [" + getName() + "]" ).start();
691 }
692
693 public void init(LoadTestRunContext context)
694 {
695 writers.clear();
696
697 String statisticsLogFolder = context.expand( getStatisticsLogFolder() );
698 if( StringUtils.isNullOrEmpty( statisticsLogFolder ))
699 return;
700
701 File folder = new File( statisticsLogFolder );
702 if( !folder.exists() )
703 {
704 if( !folder.mkdirs())
705 {
706 SoapUI.logError( new Exception( "Failed to create statistics log folder [" + statisticsLogFolder + "]") );
707 return;
708 }
709 }
710
711 for( int c = 0; c < testCase.getTestStepCount(); c++ )
712 {
713 try
714 {
715 WsdlTestStep testStep = testCase.getTestStepAt( c );
716 String fileName = StringUtils.createFileName( testStep.getName(), '_' ) + ".log";
717 PrintWriter writer = new PrintWriter( new File( folder, fileName ));
718 writers.add( writer );
719 addHeaders( writer );
720 }
721 catch( FileNotFoundException e )
722 {
723 e.printStackTrace();
724 writers.add( null );
725 }
726 }
727
728
729 try
730 {
731 String fileName = StringUtils.createFileName( testCase.getName(), '_' ) + ".log";
732 writers.add( new PrintWriter( new File( folder, fileName )) );
733 }
734 catch( FileNotFoundException e )
735 {
736 e.printStackTrace();
737 }
738
739 startTime = System.nanoTime();
740 }
741
742 private void addHeaders( PrintWriter writer )
743 {
744 writer.print( "date,threads,elapsed,min,max,avg,last,cnt,tps,bytes,bps,err,reason\n" );
745 }
746
747 public void finish()
748 {
749 stopped = true;
750
751 logStatistics( "Finished" );
752 for( PrintWriter writer : writers )
753 {
754 if( writer != null )
755 writer.close();
756 }
757 }
758
759 private synchronized void logStatistics( String trigger )
760 {
761 if( writers.isEmpty() )
762 return;
763
764 long timestamp = System.nanoTime();
765 String elapsedString = String.valueOf( (timestamp-startTime)/100000 );
766 String dateString = new Date().toString();
767 String threadCountString = String.valueOf( getThreadCount());
768
769 StringList[] snapshot = statisticsModel.getSnapshot();
770 for( int c = 0; c < snapshot.length; c++ )
771 {
772 PrintWriter writer = writers.get( c );
773 if( writer == null )
774 continue;
775
776 StringList values = snapshot[c];
777 writer.append( dateString ).append( ',' );
778 writer.append( threadCountString).append( ',' );
779 writer.append( elapsedString );
780
781 for( String value : values )
782 {
783 writer.append( ',' ).append( value );
784 }
785
786 writer.append( ',' ).append( trigger ).append( '\n' );
787 writer.flush();
788 }
789 }
790 }
791 }