View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2007 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.util.ArrayList;
18  import java.util.HashSet;
19  import java.util.LinkedList;
20  import java.util.List;
21  import java.util.Set;
22  
23  import org.apache.log4j.Logger;
24  
25  import com.eviware.soapui.SoapUI;
26  import com.eviware.soapui.config.LoadStrategyConfig;
27  import com.eviware.soapui.config.LoadTestAssertionConfig;
28  import com.eviware.soapui.config.LoadTestConfig;
29  import com.eviware.soapui.config.LoadTestLimitTypesConfig;
30  import com.eviware.soapui.config.LoadTestLimitTypesConfig.Enum;
31  import com.eviware.soapui.impl.wsdl.AbstractWsdlModelItem;
32  import com.eviware.soapui.impl.wsdl.loadtest.assertions.AbstractLoadTestAssertion;
33  import com.eviware.soapui.impl.wsdl.loadtest.assertions.LoadTestAssertionRegistry;
34  import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics;
35  import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLog;
36  import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLogErrorEntry;
37  import com.eviware.soapui.impl.wsdl.loadtest.strategy.BurstLoadStrategy;
38  import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategy;
39  import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategyFactory;
40  import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategyRegistry;
41  import com.eviware.soapui.impl.wsdl.loadtest.strategy.SimpleLoadStrategy;
42  import com.eviware.soapui.impl.wsdl.support.Configurable;
43  import com.eviware.soapui.impl.wsdl.testcase.WsdlTestCase;
44  import com.eviware.soapui.model.support.LoadTestRunListenerAdapter;
45  import com.eviware.soapui.model.testsuite.LoadTest;
46  import com.eviware.soapui.model.testsuite.LoadTestRunContext;
47  import com.eviware.soapui.model.testsuite.LoadTestRunListener;
48  import com.eviware.soapui.model.testsuite.LoadTestRunner;
49  import com.eviware.soapui.model.testsuite.TestRunContext;
50  import com.eviware.soapui.model.testsuite.TestRunner;
51  import com.eviware.soapui.model.testsuite.TestStepResult;
52  import com.eviware.soapui.model.testsuite.LoadTestRunner.Status;
53  
54  /***
55   * TestCase implementation for LoadTests
56   *  
57   * @todo add assertionFailed event to LoadTestListener
58   * @todo create and return LoadTestAssertionResult from load-test assertions 
59   *  
60   * @author Ole.Matzura
61   */
62  
63  public class WsdlLoadTest extends AbstractWsdlModelItem<LoadTestConfig> implements LoadTest
64  {
65  	public final static String THREADCOUNT_PROPERTY = WsdlLoadTest.class.getName() + "@threadcount";
66  	public final static String STARTDELAY_PROPERTY = WsdlLoadTest.class.getName() + "@startdelay";
67  	public final static String TESTLIMIT_PROPERTY = WsdlLoadTest.class.getName() + "@testlimit";
68  	public final static String HISTORYLIMIT_PROPERTY = WsdlLoadTest.class.getName() + "@historylimit";
69  	public final static String LIMITTYPE_PROPERRY = WsdlLoadTest.class.getName() + "@limittype";
70  	public final static String SAMPLEINTERVAL_PROPERRY = WsdlLoadTest.class.getName() + "@sample-interval";
71  	public static final String MAXASSERTIONERRORS_PROPERTY = WsdlLoadTest.class.getName() + "@max-assertion-errors";
72  
73     private final static Logger logger = Logger.getLogger( WsdlLoadTest.class );
74  	
75     private InternalTestRunListener internalTestRunListener = new InternalTestRunListener();
76     
77  	private WsdlTestCase testCase;
78  	private LoadTestStatistics statisticsModel;
79  	private LoadStrategy loadStrategy = new BurstLoadStrategy();
80  	private LoadTestLog loadTestLog;
81  	
82  	private LoadStrategyConfigurationChangeListener loadStrategyListener = new LoadStrategyConfigurationChangeListener();
83  	private List<LoadTestAssertion> assertions = new ArrayList<LoadTestAssertion>();
84  	private ConfigurationChangePropertyListener configurationChangeListener = new ConfigurationChangePropertyListener();
85  	private Set<LoadTestListener> loadTestListeners = new HashSet<LoadTestListener>();
86  	private Set<LoadTestRunListener> loadTestRunListeners = new HashSet<LoadTestRunListener>();
87  	private List<LoadTestLogErrorEntry> assertionErrors = new LinkedList<LoadTestLogErrorEntry>();
88  	private WsdlLoadTestRunner runner;
89     
90     public WsdlLoadTest(WsdlTestCase testCase, LoadTestConfig config )
91     {
92     	super( config, testCase, "/loadTest.gif" );
93     	
94        this.testCase = testCase;
95  		
96  		if( getConfig().getThreadCount() < 1 )
97  			getConfig().setThreadCount( 5 );
98  		
99  		if( getConfig().getLimitType() == null )
100 		{
101 			getConfig().setLimitType( LoadTestLimitTypesConfig.TIME );
102 			getConfig().setTestLimit( 60 );
103 		}	
104 
105 		if( !getConfig().isSetHistoryLimit() )
106 		{
107 			getConfig().setHistoryLimit( -1 );
108 		}
109 		
110       addLoadTestRunListener( internalTestRunListener );
111       
112       LoadStrategyConfig ls = getConfig().getLoadStrategy();
113       if( ls == null )
114       {
115       	ls = getConfig().addNewLoadStrategy();
116       	ls.setType( SimpleLoadStrategy.STRATEGY_TYPE );
117       }
118       
119       LoadStrategyFactory factory = LoadStrategyRegistry.getInstance().getFactory( ls.getType() );
120       if( factory == null )
121       {
122       	ls.setType( SimpleLoadStrategy.STRATEGY_TYPE );
123       	factory = LoadStrategyRegistry.getInstance().getFactory( ls.getType() );
124       }
125       
126       loadStrategy = factory.build( ls.getConfig() );
127       loadStrategy.addConfigurationChangeListener( loadStrategyListener );
128       
129       addLoadTestRunListener( loadStrategy );
130       
131       statisticsModel = new LoadTestStatistics( this );
132       
133       if( getConfig().xgetSampleInterval() == null )
134       	setSampleInterval( LoadTestStatistics.DEFAULT_SAMPLE_INTERVAL );
135       
136       statisticsModel.setUpdateFrequency( getSampleInterval() );
137       
138       List<LoadTestAssertionConfig> assertionList = getConfig().getAssertionList();
139       for( LoadTestAssertionConfig assertionConfig : assertionList )
140       {
141       	AbstractLoadTestAssertion assertion = LoadTestAssertionRegistry.buildAssertion( assertionConfig, this );
142       	if( assertion != null )
143       	{
144       		assertions.add( assertion);
145       		assertion.addPropertyChangeListener( LoadTestAssertion.CONFIGURATION_PROPERTY, configurationChangeListener );
146       	}
147       	else
148       	{
149       		logger.warn( "Failed to build LoadTestAssertion from getConfig() [" + assertionConfig + "]" );
150       	}
151       }
152       
153       if( getConfig().xgetResetStatisticsOnThreadCountChange() == null )
154       	getConfig().setResetStatisticsOnThreadCountChange( true );
155 
156       if( getConfig().xgetCalculateTPSOnTimePassed() == null )
157       	getConfig().setCalculateTPSOnTimePassed( false );
158       
159       if( !getConfig().isSetMaxAssertionErrors())
160       	getConfig().setMaxAssertionErrors( 100 );
161       
162       loadTestLog = new LoadTestLog( this );
163       
164       for (LoadTestRunListener listener : SoapUI.getListenerRegistry().getListeners( LoadTestRunListener.class ))
165       {
166           addLoadTestRunListener(listener);
167       }
168    }
169 
170    public LoadTestStatistics getStatisticsModel()
171    {
172    	return statisticsModel;
173    }
174    
175    public long getThreadCount()
176    {
177    	return getConfig().getThreadCount();
178    }
179    
180    public void setThreadCount( long threadCount )
181    {
182    	if( threadCount < 1 || threadCount == getThreadCount() )
183    		return;
184    	
185    	long oldCount = getThreadCount();
186    	getConfig().setThreadCount( (int) threadCount );
187    	notifyPropertyChanged( THREADCOUNT_PROPERTY, oldCount, threadCount );
188    }
189    
190    public boolean getResetStatisticsOnThreadCountChange()
191    {
192    	return getConfig().getResetStatisticsOnThreadCountChange();
193    }
194    
195    public void setResetStatisticsOnThreadCountChange( boolean value )
196    {
197    	getConfig().setResetStatisticsOnThreadCountChange( value );
198    }   
199 
200    public boolean getCalculateTPSOnTimePassed()
201    {
202    	return getConfig().getCalculateTPSOnTimePassed();
203    }
204    
205    public void setCalculateTPSOnTimePassed( boolean value )
206    {
207    	getConfig().setCalculateTPSOnTimePassed( value );
208    }   
209 
210    public int getStartDelay()
211    {
212    	return getConfig().getStartDelay();
213    }
214    
215    public void setStartDelay( int startDelay )
216    {
217    	if( startDelay < 0 )
218    		return;
219    	
220    	int oldDelay = getStartDelay();
221    	getConfig().setStartDelay( startDelay );
222    	notifyPropertyChanged( STARTDELAY_PROPERTY, oldDelay, startDelay );
223    }
224 
225    public long getHistoryLimit()
226    {
227    	return getConfig().getHistoryLimit();
228    }
229    
230    public void setHistoryLimit( long historyLimit )
231    {
232    	long oldLimit = getHistoryLimit();
233    	getConfig().setHistoryLimit( historyLimit );
234    	if( historyLimit == 0 )
235    	
236    	
237    	notifyPropertyChanged( HISTORYLIMIT_PROPERTY, oldLimit, historyLimit );
238    }
239    
240    public long getTestLimit()
241    {
242    	return getConfig().getTestLimit();
243    }
244    
245    public void setTestLimit( long testLimit )
246    {
247    	if( testLimit < 0 )
248    		return;
249    	
250    	long oldLimit = getTestLimit();
251    	getConfig().setTestLimit( testLimit );
252    	notifyPropertyChanged( TESTLIMIT_PROPERTY, oldLimit, testLimit );
253    }
254    
255    public long getMaxAssertionErrors()
256    {
257    	return getConfig().getMaxAssertionErrors();
258    }
259    
260    public void setMaxAssertionErrors( long testLimit )
261    {
262    	if( testLimit < 0 )
263    		return;
264    	
265    	long oldLimit = getMaxAssertionErrors();
266    	getConfig().setMaxAssertionErrors( testLimit );
267    	notifyPropertyChanged( MAXASSERTIONERRORS_PROPERTY, oldLimit, testLimit );
268    }
269    
270    public long getSampleInterval()
271    {
272    	return getConfig().getSampleInterval();
273    }
274    
275    public void setSampleInterval( int sampleInterval )
276    {
277    	if( sampleInterval < 0 )
278    		return;
279    	
280    	long oldInterval = getSampleInterval();
281    	getConfig().setSampleInterval( sampleInterval );
282    	
283    	statisticsModel.setUpdateFrequency( sampleInterval );
284    	
285    	notifyPropertyChanged( TESTLIMIT_PROPERTY, oldInterval, sampleInterval );
286    }
287 
288    public Enum getLimitType()
289    {
290    	return getConfig().getLimitType();
291    }
292    
293    public void setLimitType( Enum limitType )
294    {
295    	if( limitType == null )
296    		return;
297    	
298    	Enum oldType = getLimitType();
299    	getConfig().setLimitType( limitType );
300    	notifyPropertyChanged( LIMITTYPE_PROPERRY, oldType, limitType );
301    }
302    
303    public WsdlTestCase getTestCase()
304 	{
305 		return testCase;
306 	}
307 
308    public synchronized WsdlLoadTestRunner run() 
309    {
310    	getStatisticsModel().reset();
311    	if( runner != null && runner.getStatus() == Status.RUNNING )
312    		return null;
313    	
314    	assertionErrors.clear();
315    	runner = new WsdlLoadTestRunner( this );
316    	runner.start();
317    	return runner;
318 	}
319 
320 	private class InternalTestRunListener extends LoadTestRunListenerAdapter
321 	{
322 		public void afterTestCase(LoadTestRunner loadTestRunner, LoadTestRunContext context, TestRunner testRunner, TestRunContext runContext)
323 		{
324 			if( !assertions.isEmpty() )
325 			{
326 				for( LoadTestAssertion assertion : assertions )
327 				{
328 					String error = assertion.assertResults( loadTestRunner, context, testRunner, runContext );
329 					if( error != null )
330 					{
331 						loadTestLog.addEntry( new LoadTestLogErrorEntry( assertion.getName(), error, assertion.getIcon() ));
332 						statisticsModel.addError( LoadTestStatistics.TOTAL );
333 					}	
334 				}
335 			}				
336 		}
337 
338 		public void afterTestStep(LoadTestRunner loadTestRunner, LoadTestRunContext context, TestRunner testRunner, TestRunContext runContext, TestStepResult result)
339 		{
340 			if( !assertions.isEmpty() )
341 			{
342 				boolean added = false;
343 				
344 				for( LoadTestAssertion assertion : assertions )
345 				{
346 					String error = assertion.assertResult( loadTestRunner, context, result, testRunner, runContext );
347 					if( error != null )
348 					{
349 						int indexOfTestStep = testRunner.getTestCase().getIndexOfTestStep( result.getTestStep() );
350 						
351 						LoadTestLogErrorEntry errorEntry = new LoadTestLogErrorEntry( assertion.getName(), error, result, assertion.getIcon() );
352 						loadTestLog.addEntry( errorEntry);
353 						statisticsModel.addError( indexOfTestStep );
354 						
355 						long maxAssertionErrors = getMaxAssertionErrors();
356 						if( maxAssertionErrors > 0 )
357 						{
358 							synchronized( assertionErrors )
359 							{
360 								assertionErrors.add( errorEntry );
361 								while( assertionErrors.size() > maxAssertionErrors )
362 								{
363 									assertionErrors.remove( 0 ).discard();
364 								}
365 							}
366 						}
367 						
368 						added = true;
369 					}	
370 				}
371 
372 				// always discard result if there were no errors
373 				if( !added )
374 				{
375 					result.discard();
376 				}
377 			}				
378 			else result.discard();
379 		}
380 	}
381 
382 	public LoadStrategy getLoadStrategy()
383 	{
384 		return loadStrategy;
385 	}
386 	
387 	public void setLoadStrategy( LoadStrategy loadStrategy )
388 	{
389 		this.loadStrategy.removeConfigurationChangeListener( loadStrategyListener );
390 		removeLoadTestRunListener( this.loadStrategy );
391 		
392 		this.loadStrategy = loadStrategy;
393 		this.loadStrategy.addConfigurationChangeListener( loadStrategyListener );
394 		addLoadTestRunListener( this.loadStrategy );
395 		
396 		getConfig().getLoadStrategy().setType( loadStrategy.getType() );
397 		getConfig().getLoadStrategy().setConfig( loadStrategy.getConfig() );
398 	}
399 
400 	private class LoadStrategyConfigurationChangeListener implements PropertyChangeListener
401 	{
402 		public void propertyChange(PropertyChangeEvent evt)
403 		{
404 			getConfig().getLoadStrategy().setConfig( loadStrategy.getConfig() );
405 		}
406 	}
407 
408 	public LoadTestAssertion addAssertion( String type, String targetStep, boolean showConfig )
409 	{
410 		LoadTestAssertion assertion = LoadTestAssertionRegistry.createAssertion( type, this );
411 		assertion.setTargetStep( targetStep );
412 		
413 		if( assertion instanceof Configurable && showConfig )
414 		{
415 			if( !((Configurable)assertion).configure() )
416 				return null;
417 		}
418 		
419 		assertions.add( assertion );
420 		
421 		getConfig().addNewAssertion().set( assertion.getConfiguration() );
422 		assertion.addPropertyChangeListener( LoadTestAssertion.CONFIGURATION_PROPERTY, configurationChangeListener );
423 		fireAssertionAdded( assertion );
424 		
425 		return assertion;
426 	}
427 	
428 	public void removeAssertion( LoadTestAssertion assertion)
429 	{
430 		int ix = assertions.indexOf( assertion );
431 		if( ix >= 0 )
432 		{
433 			try
434 			{
435 				assertions.remove( ix );
436 				fireAssertionRemoved(assertion);
437 			}
438 			finally
439 			{
440 				assertion.removePropertyChangeListener( configurationChangeListener );
441 				assertion.release();
442 				getConfig().removeAssertion( ix );
443 			}
444 		}
445 	}
446 
447 	private void fireAssertionRemoved(LoadTestAssertion assertion)
448 	{
449 		if( !loadTestListeners.isEmpty() )
450 		{
451 			LoadTestListener[] l = loadTestListeners.toArray( new LoadTestListener[loadTestListeners.size()] );
452 			for( LoadTestListener listener : l )
453 			{
454 				listener.assertionRemoved( assertion );
455 			}
456 		}
457 	}
458 	
459 	private void fireAssertionAdded(LoadTestAssertion assertion)
460 	{
461 		if( !loadTestListeners.isEmpty() )
462 		{
463 			LoadTestListener[] l = loadTestListeners.toArray( new LoadTestListener[loadTestListeners.size()] );
464 			for( LoadTestListener listener : l )
465 			{
466 				listener.assertionAdded( assertion );
467 			}
468 		}
469 	}
470 
471 	public int getAssertionCount()
472 	{
473 		return assertions.size();
474 	}
475 	
476 	public LoadTestAssertion getAssertionAt( int index )
477 	{
478 		return index < 0 || index >= assertions.size() ? null : assertions.get( index );
479 	}
480 	
481 	private class ConfigurationChangePropertyListener implements PropertyChangeListener
482 	{
483 		public void propertyChange(PropertyChangeEvent evt)
484 		{
485 			 int ix = assertions.indexOf( evt.getSource() );
486 			 if( ix >= 0 )
487 			 {
488 				 getConfig().getAssertionArray( ix ).set( assertions.get( ix ).getConfiguration() );
489 			 }
490 		}
491 	}
492 
493 	public LoadTestLog getLoadTestLog()
494 	{
495 		return loadTestLog;
496 	}
497 
498 	public List<LoadTestAssertion> getAssertionList()
499 	{
500 		return assertions;
501 	}
502 	
503 	public void addLoadTestListener( LoadTestListener listener )
504 	{
505 		loadTestListeners.add( listener );
506 	}
507 
508 	public void removeLoadTestListener( LoadTestListener listener )
509 	{
510 		loadTestListeners.remove( listener );
511 	}
512 
513 	public void addLoadTestRunListener(LoadTestRunListener listener)
514 	{
515 		loadTestRunListeners.add( listener );
516 	}
517 
518 	public void removeLoadTestRunListener(LoadTestRunListener listener)
519 	{
520 		loadTestRunListeners.remove( listener );
521 	}
522 	
523 	public LoadTestRunListener [] getLoadTestRunListeners()
524 	{
525 		return loadTestRunListeners.toArray( new LoadTestRunListener[loadTestRunListeners.size()] );
526 	}
527 
528 	/***
529 	 * Release internal objects so they can remove listeners
530 	 */
531 	
532 	public void release()
533 	{
534 		super.release();
535 		
536 		statisticsModel.release();
537 		loadTestLog.release();
538 		
539 		for( LoadTestAssertion assertion : assertions )
540 			assertion.release();
541 		
542 		loadTestRunListeners.clear();
543 		loadTestListeners.clear();
544 	}
545 
546 	public boolean isRunning()
547 	{
548 		return runner != null && runner.getStatus() == LoadTestRunner.Status.RUNNING;
549 	}
550 
551 	public WsdlLoadTestRunner getRunner()
552 	{
553 		return runner;
554 	}
555 
556 	public void resetConfigOnMove( LoadTestConfig config )
557 	{
558 		setConfig( config );
559 		
560 		loadStrategy.updateConfig( config.getLoadStrategy().getConfig() );
561 		
562 		List<LoadTestAssertionConfig> assertionList = config.getAssertionList();
563 		for( int c = 0; c < assertionList.size(); c++ )
564 		{
565 			assertions.get( c ).updateConfiguration( assertionList.get( c ) );
566 		}
567 	}
568 }