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