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.panels.loadtest;
14  
15  import java.awt.BorderLayout;
16  import java.awt.Color;
17  import java.awt.Dimension;
18  import java.awt.event.ActionEvent;
19  import java.awt.event.ItemEvent;
20  import java.awt.event.ItemListener;
21  import java.beans.PropertyChangeEvent;
22  import java.beans.PropertyChangeListener;
23  
24  import javax.swing.AbstractAction;
25  import javax.swing.Action;
26  import javax.swing.BorderFactory;
27  import javax.swing.Box;
28  import javax.swing.JButton;
29  import javax.swing.JComboBox;
30  import javax.swing.JComponent;
31  import javax.swing.JLabel;
32  import javax.swing.JPanel;
33  import javax.swing.JProgressBar;
34  import javax.swing.JSpinner;
35  import javax.swing.JSplitPane;
36  import javax.swing.JTabbedPane;
37  import javax.swing.SpinnerNumberModel;
38  import javax.swing.event.ChangeEvent;
39  import javax.swing.event.ChangeListener;
40  
41  import com.eviware.soapui.SoapUI;
42  import com.eviware.soapui.config.LoadTestLimitTypesConfig;
43  import com.eviware.soapui.impl.wsdl.actions.loadtest.LoadTestOptionsAction;
44  import com.eviware.soapui.impl.wsdl.actions.support.ShowOnlineHelpAction;
45  import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTest;
46  import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTestRunner;
47  import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics;
48  import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics.Statistic;
49  import com.eviware.soapui.impl.wsdl.loadtest.data.actions.ExportStatisticsAction;
50  import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLog;
51  import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategy;
52  import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategyFactory;
53  import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategyRegistry;
54  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
55  import com.eviware.soapui.model.ModelItem;
56  import com.eviware.soapui.model.support.LoadTestRunListenerAdapter;
57  import com.eviware.soapui.model.testsuite.LoadTestRunContext;
58  import com.eviware.soapui.model.testsuite.LoadTestRunListener;
59  import com.eviware.soapui.model.testsuite.LoadTestRunner;
60  import com.eviware.soapui.model.testsuite.LoadTestRunner.Status;
61  import com.eviware.soapui.support.UISupport;
62  import com.eviware.soapui.support.components.JXToolBar;
63  import com.eviware.soapui.ui.desktop.DesktopPanel;
64  import com.eviware.soapui.ui.support.DesktopListenerAdapter;
65  import com.eviware.soapui.ui.support.ModelItemDesktopPanel;
66  import com.jgoodies.forms.builder.ButtonBarBuilder;
67  
68  /***
69   * Desktop panel for LoadTests
70   * 
71   * @author Ole.Matzura
72   */
73  
74  public class WsdlLoadTestDesktopPanel extends ModelItemDesktopPanel<WsdlLoadTest> implements PropertyChangeListener
75  {
76  	private JPanel contentPanel;
77  	private JSplitPane mainSplit;
78  	private JTabbedPane mainTabs;
79  	private JPanel graphPanel;
80  	private JButton runButton;
81  	private JButton cancelButton;
82  	private JButton statisticsGraphButton;
83  	private WsdlLoadTestRunner runner;
84  	private JSpinner threadsSpinner;
85  	private LoadTestRunListener internalLoadTestListener = new InternalLoadTestListener();
86  	private JComboBox strategyCombo;
87  	private JPanel loadStrategyConfigurationPanel;
88  	private JButton resetButton;
89  	private LoadTestLog loadTestLog;
90  	private JButton optionsButton;
91  	private JButton testTimesGraphButton;
92  	private Object limit;
93  	private JSpinner limitSpinner;
94  	private JComboBox limitTypeCombo;
95  	private SpinnerNumberModel limitSpinnerModel;
96  	private JProgressBar progressBar;
97  	private long loadTestStartTime;
98  	private StatisticsDesktopPanel statisticsDesktopPanel;
99  	private StatisticsHistoryDesktopPanel statisticsHistoryDesktopPanel;
100 	
101 	public boolean loadTestIsRunning;
102 	private InternalDesktopListener desktopListener;
103 	private JButton exportButton;
104 	private JLoadTestAssertionsTable assertionsTable;
105 	
106 	public WsdlLoadTestDesktopPanel(WsdlLoadTest loadTest)
107 	{
108 		super( loadTest );
109 		
110 		loadTestLog = loadTest.getLoadTestLog();
111 		loadTest.addPropertyChangeListener( WsdlLoadTest.THREADCOUNT_PROPERTY, this );
112 		loadTest.addLoadTestRunListener( internalLoadTestListener );
113 		
114 		desktopListener = new InternalDesktopListener();
115 	//	SoapUI.getDesktop().addDesktopListener( desktopListener );
116 		
117 		buildUI();
118 	}
119 
120 	private void buildUI()
121 	{
122 		contentPanel = new JPanel( new BorderLayout());
123 		
124 		contentPanel.add( buildToolbar(), BorderLayout.NORTH );
125 		contentPanel.add( buildContent(), BorderLayout.CENTER );
126 		
127 		contentPanel.setPreferredSize( new Dimension( 600, 500 ));
128 	}
129 
130 	private JComponent buildContent()
131 	{
132 		JTabbedPane tabbedPane = new JTabbedPane( JTabbedPane.BOTTOM );
133 		tabbedPane.addTab( "LoadTest Log", buildLog() );
134 		tabbedPane.addTab( "LoadTest Assertions", buildAssertions() );
135 		JPanel p = new JPanel( new BorderLayout() );
136 		p.add( tabbedPane, BorderLayout.CENTER );
137 		p.setBackground( Color.LIGHT_GRAY );
138 		
139 		JSplitPane mainSplit = UISupport.createVerticalSplit( buildStatistics(), p );
140 		mainSplit.setDividerLocation( 150 );
141 		return mainSplit;
142 	}
143 
144 	private JComponent buildStatistics(	)
145 	{
146 		return new JStatisticsTable( getModelItem() );
147 	}
148 
149 	private JComponent buildLog()
150 	{
151 		JLoadTestLogTable loadTestLogTable = new JLoadTestLogTable( loadTestLog );
152 		return loadTestLogTable;
153 	}
154 
155 	private JComponent buildAssertions()
156 	{
157 		assertionsTable = new JLoadTestAssertionsTable( getModelItem() );
158 		return assertionsTable;
159 	}
160 
161 	private JComponent buildToolbar()
162 	{
163 		WsdlLoadTest loadTest = getModelItem();
164 		
165 		JXToolBar toolbar = UISupport.createToolbar();
166 		
167 		//ButtonBarBuilder builder = new ButtonBarBuilder();
168       runButton = UISupport.createToolbarButton( new RunLoadTestAction());
169       cancelButton = UISupport.createToolbarButton( new CancelRunTestCaseAction(), false );
170       resetButton = UISupport.createToolbarButton( new ResetAction() );
171       exportButton = UISupport.createToolbarButton( new ExportStatisticsAction( loadTest.getStatisticsModel() ) );
172       
173       statisticsGraphButton = UISupport.createToolbarButton( new ShowStatisticsGraphAction() );
174       testTimesGraphButton = UISupport.createToolbarButton( new ShowTestTimesGraphAction() );
175       optionsButton = UISupport.createToolbarButton( new LoadTestOptionsAction( getModelItem() ) );
176       
177       strategyCombo = new JComboBox( LoadStrategyRegistry.getInstance().getStrategies() );
178       UISupport.setPreferredHeight( strategyCombo, 18 );
179 		strategyCombo.setSelectedItem( loadTest.getLoadStrategy().getType());
180 		strategyCombo.addItemListener( new ItemListener() 
181 				{
182 					public void itemStateChanged(ItemEvent e)
183 					{
184 						Object item = e.getItem();
185 						if( item == null ) 
186 							return;
187 						
188 						setLoadStrategy(item.toString());
189 					}} );
190       
191 		toolbar.add( runButton );
192 		toolbar.add( cancelButton );
193 		toolbar.add( statisticsGraphButton );
194 		toolbar.add( testTimesGraphButton );
195 		toolbar.add( resetButton );
196 		toolbar.add( exportButton );
197      
198 		toolbar.add( optionsButton );
199 		toolbar.add( UISupport.createToolbarButton( new ShowOnlineHelpAction( HelpUrls.LOADTESTEDITOR_HELP_URL )));
200 		toolbar.add( Box.createHorizontalGlue() );
201 		buildLimitBar( toolbar );
202       toolbar.addSeparator();
203 		
204 		progressBar = new JProgressBar( 0, 100 );
205 		progressBar.setPreferredSize( new Dimension( 70, 20 ));
206 
207 		toolbar.addFixed( progressBar );
208 		
209 		ButtonBarBuilder builder = new ButtonBarBuilder();
210 		
211 		builder.addFixed( new JLabel( "Threads:" ));
212       builder.addRelatedGap();
213 		
214 		threadsSpinner = new JSpinner( new SpinnerNumberModel(getModelItem().getThreadCount(), 1, 9999, 1) );
215 		UISupport.setPreferredHeight( threadsSpinner, 18 );
216       threadsSpinner.getModel().addChangeListener( new ChangeListener() {
217 
218 			public void stateChanged(ChangeEvent e)
219 			{
220 				getModelItem().setThreadCount( ((SpinnerNumberModel) threadsSpinner.getModel()).getNumber().intValue() );
221 			}} );
222       
223 		builder.addFixed( threadsSpinner);
224 		builder.addUnrelatedGap();
225 		
226 		LoadStrategy loadStrategy = loadTest.getLoadStrategy();
227 		
228 		builder.addFixed( new JLabel( "Strategy" ));
229       builder.addRelatedGap();
230       builder.addFixed( strategyCombo );
231       builder.addUnrelatedGap();
232 		
233       loadStrategyConfigurationPanel = new JPanel( new BorderLayout() );
234 		loadStrategyConfigurationPanel.add( loadStrategy.getConfigurationPanel(), BorderLayout.CENTER );
235       
236       builder.addFixed( loadStrategyConfigurationPanel );
237       builder.setBorder( BorderFactory.createEmptyBorder( 2, 3, 3, 3 ) );
238       
239 		return UISupport.buildPanelWithToolbar( toolbar, builder.getPanel() );
240 	}
241 	
242 	public void buildLimitBar( JXToolBar toolbar )
243 	{
244 		limitSpinnerModel = new SpinnerNumberModel(getModelItem().getTestLimit(), 0, Long.MAX_VALUE, 100 );
245 		
246 		limitSpinner = new JSpinner( limitSpinnerModel );
247 		limitSpinner.setPreferredSize( new Dimension( 70, 20 ));
248 		limitSpinner.setToolTipText( "Sets the limit for this test; requests per thread or seconds" );
249 		limitSpinner.getModel().addChangeListener( new ChangeListener() {
250 
251 			public void stateChanged(ChangeEvent e)
252 			{
253 				int intValue = ((SpinnerNumberModel) limitSpinner.getModel()).getNumber().intValue();
254 				getModelItem().setTestLimit( intValue );
255 			}} );
256 		
257 		toolbar.addSeparator();
258 		toolbar.addFixed( new JLabel( "Limit:" ));
259 		toolbar.addSeparator();
260 		toolbar.addFixed(limitSpinner );
261 		toolbar.addSeparator();
262 		
263 		limitTypeCombo = new JComboBox( new String[] { "Runs", "Seconds"});
264 		
265 		if( getModelItem().getLimitType() == LoadTestLimitTypesConfig.TIME )
266 			limitTypeCombo.setSelectedIndex( 1 );
267 		
268 		toolbar.addFixed( limitTypeCombo);
269 		toolbar.addSeparator();
270 		
271 		limitTypeCombo.addItemListener( new ItemListener() 
272 		{
273 			public void itemStateChanged(ItemEvent e)
274 			{
275 				Object item = e.getItem();
276 				if( "Runs".equals( item ))
277 				{
278 					getModelItem().setLimitType( LoadTestLimitTypesConfig.COUNT );
279 				}
280 				else if( "Seconds".equals( item ))
281 				{
282 					getModelItem().setLimitType( LoadTestLimitTypesConfig.TIME );
283 				}
284 			}} );
285 	}
286 
287 	public boolean onClose( boolean canCancel )
288 	{
289 		if( runner != null && runner.getStatus() == Status.RUNNING )
290 		{
291 			if( !UISupport.confirm( "Running test will be canceled when closing window. Close anyway?", "Close LoadTest" ))
292 				return false;
293 		}
294 		
295 		getModelItem().removeLoadTestRunListener( internalLoadTestListener );
296 		getModelItem().removePropertyChangeListener( WsdlLoadTest.THREADCOUNT_PROPERTY, this );
297 		
298 		if( runner != null && runner.getStatus() == Status.RUNNING )
299 			runner.cancel( "closing window" );
300 		
301 		if( statisticsDesktopPanel != null )
302 			SoapUI.getDesktop().closeDesktopPanel( statisticsDesktopPanel );
303 		
304 		if( statisticsHistoryDesktopPanel != null )
305 			SoapUI.getDesktop().closeDesktopPanel( statisticsHistoryDesktopPanel );
306 		
307 		assertionsTable.release();
308 		
309 		SoapUI.getDesktop().removeDesktopListener( desktopListener );
310 		
311 		return true;
312 	}
313 
314 	public JComponent getComponent()
315 	{
316 		return contentPanel;
317 	}
318 
319    private final class InternalDesktopListener extends DesktopListenerAdapter
320 	{
321 		public void desktopPanelClosed(DesktopPanel desktopPanel)
322 		{
323 			if( desktopPanel == statisticsDesktopPanel )
324 				statisticsDesktopPanel = null;
325 			else if( desktopPanel == statisticsHistoryDesktopPanel )
326 				statisticsHistoryDesktopPanel = null;
327 		}
328 	}
329 
330 	public class RunLoadTestAction extends AbstractAction
331    {
332 		public RunLoadTestAction()
333       {
334          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/run_testcase.gif"));
335          putValue( Action.SHORT_DESCRIPTION, "Runs this LoadTest" );
336       }
337 
338 		public void actionPerformed(ActionEvent e)
339 		{
340 			if( getModelItem().getTestCase().getTestStepCount() == 0 )
341 			{
342 				UISupport.showErrorMessage( "Missing TestSteps for testing!");
343 				return;
344 			}
345 			
346 			getModelItem().getStatisticsModel().reset();
347 			loadTestLog.clear();
348 			progressBar.setValue( 0 );
349 			
350          runner = (WsdlLoadTestRunner) getModelItem().run();
351          runButton.setEnabled( false );
352 		}
353    }
354 
355    public class ResetAction extends AbstractAction
356    {
357 		public ResetAction()
358       {
359          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/reset_loadtest_statistics.gif"));
360          putValue( Action.SHORT_DESCRIPTION, "Resets statistics for this LoadTest" );
361       }
362 
363 		public void actionPerformed(ActionEvent e)
364 		{
365 			getModelItem().getStatisticsModel().reset();
366 		}
367    }
368    
369    public class ShowStatisticsGraphAction extends AbstractAction
370    {
371 		public ShowStatisticsGraphAction()
372       {
373          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/stats_graph.gif"));
374          putValue( Action.SHORT_DESCRIPTION, "Shows the statistics graph" );
375       }
376 
377 		public void actionPerformed(ActionEvent e)
378 		{
379 			if( statisticsDesktopPanel == null )
380 				statisticsDesktopPanel = new StatisticsDesktopPanel( getModelItem() );
381 			
382          UISupport.showDesktopPanel( statisticsDesktopPanel );
383 		}
384    }
385 
386    public class ShowTestTimesGraphAction extends AbstractAction
387    {
388 		public ShowTestTimesGraphAction()
389       {
390          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/samples_graph.gif"));
391          putValue( Action.SHORT_DESCRIPTION, "Shows the Statistics History graph" );
392       }
393 
394 		public void actionPerformed(ActionEvent e)
395 		{
396 			if( statisticsHistoryDesktopPanel == null )
397 				statisticsHistoryDesktopPanel = new StatisticsHistoryDesktopPanel( getModelItem() );
398 			
399          UISupport.showDesktopPanel( statisticsHistoryDesktopPanel );
400 		}
401    }
402    
403    public class CancelRunTestCaseAction extends AbstractAction
404    {
405 
406 		public CancelRunTestCaseAction()
407       {
408          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/stop_testcase.gif"));
409          putValue( Action.SHORT_DESCRIPTION, "Stops running this LoadTest" );
410       }
411       
412 		public void actionPerformed(ActionEvent e)
413 		{
414       	if( runner != null )
415       	{
416       		runner.cancel( "Canceled" );
417       	}
418       	
419       	cancelButton.setEnabled( false );
420       }
421    }
422 
423    public boolean dependsOn(ModelItem modelItem)
424 	{
425    	WsdlLoadTest loadTest = getModelItem();
426    	
427 		return modelItem == loadTest || modelItem == loadTest.getTestCase() || 
428 			modelItem == loadTest.getTestCase().getTestSuite() || 
429 			modelItem == loadTest.getTestCase().getTestSuite().getProject();
430 	}
431 	
432 	public void setLoadStrategy(String type)
433 	{
434 		LoadStrategyFactory factory = LoadStrategyRegistry.getInstance().getFactory( type );
435 		LoadStrategy loadStrategy = factory.create();
436 		getModelItem().setLoadStrategy( loadStrategy );
437 		loadStrategyConfigurationPanel.removeAll();
438 		loadStrategyConfigurationPanel.add( loadStrategy.getConfigurationPanel(), BorderLayout.CENTER );
439 		loadStrategyConfigurationPanel.revalidate();
440 	}
441 
442 	private class InternalLoadTestListener extends LoadTestRunListenerAdapter
443 	{
444 		public void beforeLoadTest( LoadTestRunner testRunner, LoadTestRunContext context )
445 		{
446          loadTestLog.clear();
447 			
448 			loadTestStartTime = System.currentTimeMillis();
449 			loadTestIsRunning = true;
450 			if( getModelItem().getTestLimit() > 0 )
451 			{
452 				progressBar.setValue(0);
453 				progressBar.setString( null );
454 			}
455 			else
456 			{
457 				progressBar.setString("...");
458 			}
459 			
460 			progressBar.setStringPainted(true);
461 
462 			runButton.setEnabled( false );
463 			cancelButton.setEnabled( true );
464 			strategyCombo.setEnabled( false );
465 			limitTypeCombo.setEnabled( false );
466 			threadsSpinner.setEnabled( getModelItem().getLoadStrategy().allowThreadCountChangeDuringRun() );
467 			
468 			new Thread( new ProgressBarUpdater() ).start();
469 		}
470 
471 		public void afterLoadTest( LoadTestRunner testRunner, LoadTestRunContext context )
472 		{
473 			runButton.setEnabled( true );
474 			
475 			cancelButton.setEnabled( false );
476 			strategyCombo.setEnabled( true );
477 			limitTypeCombo.setEnabled( true );
478 			threadsSpinner.setEnabled( true );
479 			
480 			runner = null;
481 			loadTestIsRunning = false;
482 			
483 			if( progressBar.isIndeterminate() )
484 			{
485 				progressBar.setIndeterminate( false );
486 				progressBar.setValue(0);
487 			}
488 			else if( testRunner.getStatus() == Status.FINISHED )
489 			{
490 				progressBar.setValue( 100 );
491 			}
492 			
493 			if( testRunner.getStatus() == Status.FAILED )
494 			{
495 				UISupport.showErrorMessage( "LoadTest failed" );
496 			}
497 		}
498 		
499 		
500 	}
501 
502 	public String getDescription()
503 	{
504 		return "LoadTest: [" + getModelItem().getName() + "] for TestCase [" + getModelItem().getTestCase().getName() + 
505 					"] in TestSuite [" + getModelItem().getTestCase().getTestSuite().getName() + "]";
506 	}
507 	
508 	private class ProgressBarUpdater implements Runnable
509 	{
510 		public void run()
511 		{
512 			while( true )
513 			{
514 				if( !loadTestIsRunning )
515 					break;
516 
517 				if( getModelItem().getTestLimit() == 0 )
518 				{
519 					if( loadTestIsRunning && !progressBar.isIndeterminate())
520 					{
521 						progressBar.setIndeterminate( true );
522 						progressBar.setString("...");
523 					}
524 				}
525 				else if( getModelItem().getLimitType() == LoadTestLimitTypesConfig.TIME )
526 				{
527 					if( loadTestIsRunning && progressBar.isIndeterminate())
528 					{
529 						progressBar.setIndeterminate( false );
530 						progressBar.setString(null);
531 					}
532 					
533 					long timePassed = System.currentTimeMillis()-loadTestStartTime;
534 					int value = (int) ((timePassed*100)/(getModelItem().getTestLimit()*1000));
535 					progressBar.setValue( value );
536 				}
537 				else if( getModelItem().getLimitType() == LoadTestLimitTypesConfig.COUNT )
538 				{
539 					if( loadTestIsRunning && progressBar.isIndeterminate())
540 					{
541 						progressBar.setIndeterminate( false );
542 						progressBar.setString(null);
543 					}
544 					
545 					long counts = getModelItem().getStatisticsModel().getStatistic( LoadTestStatistics.TOTAL, Statistic.COUNT );
546 					if( counts > 0 )
547 						progressBar.setValue(  (int) ((counts*100)/getModelItem().getTestLimit()) );
548 				}
549 
550 				try
551 				{
552 					Thread.sleep( 500 );
553 				}
554 				catch (InterruptedException e)
555 				{
556 					e.printStackTrace();
557 				}
558 			}
559 		}
560 	}
561 
562 	public void propertyChange(PropertyChangeEvent evt)
563 	{
564 		if( evt.getPropertyName().equals( WsdlLoadTest.THREADCOUNT_PROPERTY ))
565 		{
566 			threadsSpinner.setValue( evt.getNewValue() );
567 		}
568 	}
569 	
570 }