001: /*
002: * soapUI, copyright (C) 2004-2007 eviware.com
003: *
004: * soapUI is free software; you can redistribute it and/or modify it under the
005: * terms of version 2.1 of the GNU Lesser General Public License as published by
006: * the Free Software Foundation.
007: *
008: * soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
009: * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
010: * See the GNU Lesser General Public License for more details at gnu.org.
011: */
012:
013: package com.eviware.soapui.impl.wsdl.panels.loadtest;
014:
015: import java.awt.BorderLayout;
016: import java.awt.Color;
017: import java.awt.Dimension;
018: import java.awt.event.ActionEvent;
019: import java.awt.event.ItemEvent;
020: import java.awt.event.ItemListener;
021: import java.beans.PropertyChangeEvent;
022: import java.beans.PropertyChangeListener;
023:
024: import javax.swing.AbstractAction;
025: import javax.swing.Action;
026: import javax.swing.BorderFactory;
027: import javax.swing.Box;
028: import javax.swing.JButton;
029: import javax.swing.JComboBox;
030: import javax.swing.JComponent;
031: import javax.swing.JLabel;
032: import javax.swing.JPanel;
033: import javax.swing.JProgressBar;
034: import javax.swing.JSpinner;
035: import javax.swing.JSplitPane;
036: import javax.swing.JTabbedPane;
037: import javax.swing.SpinnerNumberModel;
038: import javax.swing.event.ChangeEvent;
039: import javax.swing.event.ChangeListener;
040:
041: import com.eviware.soapui.SoapUI;
042: import com.eviware.soapui.config.LoadTestLimitTypesConfig;
043: import com.eviware.soapui.impl.wsdl.actions.loadtest.LoadTestOptionsAction;
044: import com.eviware.soapui.impl.wsdl.actions.support.ShowOnlineHelpAction;
045: import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTest;
046: import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTestRunner;
047: import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics;
048: import com.eviware.soapui.impl.wsdl.loadtest.data.LoadTestStatistics.Statistic;
049: import com.eviware.soapui.impl.wsdl.loadtest.data.actions.ExportStatisticsAction;
050: import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLog;
051: import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategy;
052: import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategyFactory;
053: import com.eviware.soapui.impl.wsdl.loadtest.strategy.LoadStrategyRegistry;
054: import com.eviware.soapui.impl.wsdl.support.HelpUrls;
055: import com.eviware.soapui.model.ModelItem;
056: import com.eviware.soapui.model.support.LoadTestRunListenerAdapter;
057: import com.eviware.soapui.model.testsuite.LoadTestRunContext;
058: import com.eviware.soapui.model.testsuite.LoadTestRunListener;
059: import com.eviware.soapui.model.testsuite.LoadTestRunner;
060: import com.eviware.soapui.model.testsuite.LoadTestRunner.Status;
061: import com.eviware.soapui.support.UISupport;
062: import com.eviware.soapui.support.components.JXToolBar;
063: import com.eviware.soapui.ui.desktop.DesktopPanel;
064: import com.eviware.soapui.ui.support.DesktopListenerAdapter;
065: import com.eviware.soapui.ui.support.ModelItemDesktopPanel;
066: import com.jgoodies.forms.builder.ButtonBarBuilder;
067:
068: /**
069: * Desktop panel for LoadTests
070: *
071: * @author Ole.Matzura
072: */
073:
074: public class WsdlLoadTestDesktopPanel extends
075: ModelItemDesktopPanel<WsdlLoadTest> implements
076: PropertyChangeListener {
077: private static final String SECONDS_LIMIT = "Seconds";
078: private static final String RUNS_LIMIT = "Total Runs";
079: private JPanel contentPanel;
080: @SuppressWarnings("unused")
081: private JSplitPane mainSplit;
082: @SuppressWarnings("unused")
083: private JTabbedPane mainTabs;
084: @SuppressWarnings("unused")
085: private JPanel graphPanel;
086: private JButton runButton;
087: private JButton cancelButton;
088: private JButton statisticsGraphButton;
089: private WsdlLoadTestRunner runner;
090: private JSpinner threadsSpinner;
091: private LoadTestRunListener internalLoadTestListener = new InternalLoadTestListener();
092: private JComboBox strategyCombo;
093: private JPanel loadStrategyConfigurationPanel;
094: private JButton resetButton;
095: private LoadTestLog loadTestLog;
096: private JButton optionsButton;
097: private JButton testTimesGraphButton;
098: @SuppressWarnings("unused")
099: private Object limit;
100: private JSpinner limitSpinner;
101: private JComboBox limitTypeCombo;
102: private SpinnerNumberModel limitSpinnerModel;
103: private JProgressBar progressBar;
104: private long loadTestStartTime;
105: private StatisticsDesktopPanel statisticsDesktopPanel;
106: private StatisticsHistoryDesktopPanel statisticsHistoryDesktopPanel;
107:
108: public boolean loadTestIsRunning;
109: private InternalDesktopListener desktopListener;
110: private JButton exportButton;
111: private JLoadTestAssertionsTable assertionsTable;
112: private JStatisticsTable statisticsTable;
113:
114: public WsdlLoadTestDesktopPanel(WsdlLoadTest loadTest) {
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: contentPanel = new JPanel(new BorderLayout());
129:
130: contentPanel.add(buildToolbar(), BorderLayout.NORTH);
131: contentPanel.add(buildContent(), BorderLayout.CENTER);
132:
133: contentPanel.setPreferredSize(new Dimension(600, 500));
134: }
135:
136: private JComponent buildContent() {
137: JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.BOTTOM);
138: tabbedPane.addTab("LoadTest Log", buildLog());
139: tabbedPane.addTab("LoadTest Assertions", buildAssertions());
140: JPanel p = new JPanel(new BorderLayout());
141: p.add(tabbedPane, BorderLayout.CENTER);
142: p.setBackground(Color.LIGHT_GRAY);
143:
144: JSplitPane mainSplit = UISupport.createVerticalSplit(
145: buildStatistics(), p);
146: mainSplit.setDividerLocation(150);
147: return mainSplit;
148: }
149:
150: private JComponent buildStatistics() {
151: statisticsTable = new JStatisticsTable(getModelItem());
152: return statisticsTable;
153: }
154:
155: private JComponent buildLog() {
156: JLoadTestLogTable loadTestLogTable = new JLoadTestLogTable(
157: loadTestLog);
158: return loadTestLogTable;
159: }
160:
161: private JComponent buildAssertions() {
162: assertionsTable = new JLoadTestAssertionsTable(getModelItem());
163: return assertionsTable;
164: }
165:
166: private JComponent buildToolbar() {
167: WsdlLoadTest loadTest = getModelItem();
168:
169: JXToolBar toolbar = UISupport.createToolbar();
170:
171: //ButtonBarBuilder builder = new ButtonBarBuilder();
172: runButton = UISupport
173: .createToolbarButton(new RunLoadTestAction());
174: cancelButton = UISupport.createToolbarButton(
175: new CancelRunTestCaseAction(), false);
176: resetButton = UISupport.createToolbarButton(new ResetAction());
177: exportButton = UISupport
178: .createToolbarButton(new ExportStatisticsAction(
179: loadTest.getStatisticsModel()));
180:
181: statisticsGraphButton = UISupport
182: .createToolbarButton(new ShowStatisticsGraphAction());
183: testTimesGraphButton = UISupport
184: .createToolbarButton(new ShowTestTimesGraphAction());
185:
186: statisticsGraphButton.setEnabled(getModelItem()
187: .getHistoryLimit() != 0);
188: testTimesGraphButton.setEnabled(getModelItem()
189: .getHistoryLimit() != 0);
190:
191: optionsButton = UISupport
192: .createToolbarButton(new LoadTestOptionsAction(
193: getModelItem()));
194:
195: strategyCombo = new JComboBox(LoadStrategyRegistry
196: .getInstance().getStrategies());
197: strategyCombo
198: .setToolTipText("Selects which LoadTest Strategy to use");
199: UISupport.setPreferredHeight(strategyCombo, 18);
200: strategyCombo.setSelectedItem(loadTest.getLoadStrategy()
201: .getType());
202: strategyCombo.addItemListener(new ItemListener() {
203: public void itemStateChanged(ItemEvent e) {
204: Object item = e.getItem();
205: if (item == null)
206: return;
207:
208: setLoadStrategy(item.toString());
209: }
210: });
211:
212: toolbar.add(runButton);
213: toolbar.add(cancelButton);
214: toolbar.add(statisticsGraphButton);
215: toolbar.add(testTimesGraphButton);
216: toolbar.add(resetButton);
217: toolbar.add(exportButton);
218:
219: toolbar.add(optionsButton);
220: toolbar.add(UISupport
221: .createToolbarButton(new ShowOnlineHelpAction(
222: HelpUrls.LOADTESTEDITOR_HELP_URL)));
223: toolbar.add(Box.createHorizontalGlue());
224: buildLimitBar(toolbar);
225: toolbar.addSeparator();
226:
227: progressBar = new JProgressBar(0, 100);
228: progressBar.setPreferredSize(new Dimension(70, 20));
229:
230: toolbar.addFixed(progressBar);
231:
232: ButtonBarBuilder builder = new ButtonBarBuilder();
233:
234: builder.addFixed(new JLabel("Threads:"));
235: builder.addRelatedGap();
236:
237: threadsSpinner = new JSpinner(new SpinnerNumberModel(
238: getModelItem().getThreadCount(), 1, 9999, 1));
239: threadsSpinner
240: .setToolTipText("Sets the number of threads (\"Virtual Users\") to run this TestCase");
241: UISupport.setPreferredHeight(threadsSpinner, 18);
242: threadsSpinner.getModel().addChangeListener(
243: new ChangeListener() {
244:
245: public void stateChanged(ChangeEvent e) {
246: getModelItem().setThreadCount(
247: ((SpinnerNumberModel) threadsSpinner
248: .getModel()).getNumber()
249: .intValue());
250: }
251: });
252:
253: builder.addFixed(threadsSpinner);
254: builder.addUnrelatedGap();
255:
256: LoadStrategy loadStrategy = loadTest.getLoadStrategy();
257:
258: builder.addFixed(new JLabel("Strategy"));
259: builder.addRelatedGap();
260: builder.addFixed(strategyCombo);
261: builder.addUnrelatedGap();
262:
263: loadStrategyConfigurationPanel = new JPanel(new BorderLayout());
264: loadStrategyConfigurationPanel.add(loadStrategy
265: .getConfigurationPanel(), BorderLayout.CENTER);
266:
267: builder.addFixed(loadStrategyConfigurationPanel);
268: builder.setBorder(BorderFactory.createEmptyBorder(2, 3, 3, 3));
269:
270: return UISupport.buildPanelWithToolbar(toolbar, builder
271: .getPanel());
272: }
273:
274: public void buildLimitBar(JXToolBar toolbar) {
275: limitSpinnerModel = new SpinnerNumberModel(getModelItem()
276: .getTestLimit(), 0, Long.MAX_VALUE, 100);
277:
278: limitSpinner = new JSpinner(limitSpinnerModel);
279: limitSpinner.setPreferredSize(new Dimension(70, 20));
280: limitSpinner
281: .setToolTipText("Sets the limit for this test; total number of requests or seconds to run");
282: limitSpinner.getModel().addChangeListener(new ChangeListener() {
283:
284: public void stateChanged(ChangeEvent e) {
285: int intValue = ((SpinnerNumberModel) limitSpinner
286: .getModel()).getNumber().intValue();
287: getModelItem().setTestLimit(intValue);
288: }
289: });
290:
291: toolbar.addSeparator();
292: toolbar.addFixed(new JLabel("Limit:"));
293: toolbar.addSeparator();
294: toolbar.addFixed(limitSpinner);
295: toolbar.addSeparator();
296:
297: limitTypeCombo = new JComboBox(new String[] {
298: WsdlLoadTestDesktopPanel.RUNS_LIMIT,
299: WsdlLoadTestDesktopPanel.SECONDS_LIMIT });
300:
301: if (getModelItem().getLimitType() == LoadTestLimitTypesConfig.TIME)
302: limitTypeCombo.setSelectedIndex(1);
303:
304: toolbar.addFixed(limitTypeCombo);
305: toolbar.addSeparator();
306:
307: limitTypeCombo.addItemListener(new ItemListener() {
308: public void itemStateChanged(ItemEvent e) {
309: Object item = e.getItem();
310: if (WsdlLoadTestDesktopPanel.RUNS_LIMIT.equals(item)) {
311: getModelItem().setLimitType(
312: LoadTestLimitTypesConfig.COUNT);
313: } else if (WsdlLoadTestDesktopPanel.SECONDS_LIMIT
314: .equals(item)) {
315: getModelItem().setLimitType(
316: LoadTestLimitTypesConfig.TIME);
317: }
318: }
319: });
320: }
321:
322: public boolean onClose(boolean canCancel) {
323: if (runner != null && runner.getStatus() == Status.RUNNING) {
324: if (!UISupport
325: .confirm(
326: "Running test will be canceled when closing window. Close anyway?",
327: "Close LoadTest"))
328: return false;
329: }
330:
331: getModelItem().removeLoadTestRunListener(
332: internalLoadTestListener);
333: getModelItem().removePropertyChangeListener(this );
334: getModelItem().getStatisticsModel().reset();
335:
336: if (runner != null && runner.getStatus() == Status.RUNNING)
337: runner.cancel("closing window");
338:
339: if (statisticsDesktopPanel != null)
340: SoapUI.getDesktop().closeDesktopPanel(
341: statisticsDesktopPanel);
342:
343: if (statisticsHistoryDesktopPanel != null)
344: SoapUI.getDesktop().closeDesktopPanel(
345: statisticsHistoryDesktopPanel);
346:
347: assertionsTable.release();
348: loadStrategyConfigurationPanel.removeAll();
349: SoapUI.getDesktop().removeDesktopListener(desktopListener);
350:
351: statisticsTable.release();
352:
353: return release();
354: }
355:
356: public JComponent getComponent() {
357: return contentPanel;
358: }
359:
360: private final class InternalDesktopListener extends
361: DesktopListenerAdapter {
362: public void desktopPanelClosed(DesktopPanel desktopPanel) {
363: if (desktopPanel == statisticsDesktopPanel)
364: statisticsDesktopPanel = null;
365: else if (desktopPanel == statisticsHistoryDesktopPanel)
366: statisticsHistoryDesktopPanel = null;
367: }
368: }
369:
370: public class RunLoadTestAction extends AbstractAction {
371: public RunLoadTestAction() {
372: putValue(Action.SMALL_ICON, UISupport
373: .createImageIcon("/run_testcase.gif"));
374: putValue(Action.SHORT_DESCRIPTION, "Runs this LoadTest");
375: }
376:
377: public void actionPerformed(ActionEvent e) {
378: WsdlLoadTest loadtest = getModelItem();
379: if (loadtest.getTestCase().getTestStepCount() == 0) {
380: UISupport
381: .showErrorMessage("Missing TestSteps for testing!");
382: return;
383: }
384:
385: if (loadtest.getLimitType() == LoadTestLimitTypesConfig.COUNT
386: && loadtest.getTestLimit() < loadtest
387: .getThreadCount()) {
388: if (!UISupport
389: .confirm(
390: "The run limit is set to a lower count than number of threads\nRun Anyway?",
391: "Run LoadTest")) {
392: return;
393: }
394: }
395:
396: runButton.setEnabled(false);
397: runner = loadtest.run();
398: }
399: }
400:
401: public class ResetAction extends AbstractAction {
402: public ResetAction() {
403: putValue(Action.SMALL_ICON, UISupport
404: .createImageIcon("/reset_loadtest_statistics.gif"));
405: putValue(Action.SHORT_DESCRIPTION,
406: "Resets statistics for this LoadTest");
407: }
408:
409: public void actionPerformed(ActionEvent e) {
410: getModelItem().getStatisticsModel().reset();
411: }
412: }
413:
414: public class ShowStatisticsGraphAction extends AbstractAction {
415: public ShowStatisticsGraphAction() {
416: putValue(Action.SMALL_ICON, UISupport
417: .createImageIcon("/stats_graph.gif"));
418: putValue(Action.SHORT_DESCRIPTION,
419: "Shows the statistics graph");
420: }
421:
422: public void actionPerformed(ActionEvent e) {
423: if (statisticsDesktopPanel == null)
424: statisticsDesktopPanel = new StatisticsDesktopPanel(
425: getModelItem());
426:
427: UISupport.showDesktopPanel(statisticsDesktopPanel);
428: }
429: }
430:
431: public class ShowTestTimesGraphAction extends AbstractAction {
432: public ShowTestTimesGraphAction() {
433: putValue(Action.SMALL_ICON, UISupport
434: .createImageIcon("/samples_graph.gif"));
435: putValue(Action.SHORT_DESCRIPTION,
436: "Shows the Statistics History graph");
437: }
438:
439: public void actionPerformed(ActionEvent e) {
440: if (statisticsHistoryDesktopPanel == null)
441: statisticsHistoryDesktopPanel = new StatisticsHistoryDesktopPanel(
442: getModelItem());
443:
444: UISupport.showDesktopPanel(statisticsHistoryDesktopPanel);
445: }
446: }
447:
448: public class CancelRunTestCaseAction extends AbstractAction {
449:
450: public CancelRunTestCaseAction() {
451: putValue(Action.SMALL_ICON, UISupport
452: .createImageIcon("/stop_testcase.gif"));
453: putValue(Action.SHORT_DESCRIPTION,
454: "Stops running this LoadTest");
455: }
456:
457: public void actionPerformed(ActionEvent e) {
458: if (runner != null) {
459: runner.cancel("Canceled");
460: cancelButton.setEnabled(false);
461: }
462: }
463: }
464:
465: public boolean dependsOn(ModelItem modelItem) {
466: WsdlLoadTest loadTest = getModelItem();
467:
468: return modelItem == loadTest
469: || modelItem == loadTest.getTestCase()
470: || modelItem == loadTest.getTestCase().getTestSuite()
471: || modelItem == loadTest.getTestCase().getTestSuite()
472: .getProject();
473: }
474:
475: public void setLoadStrategy(String type) {
476: LoadStrategyFactory factory = LoadStrategyRegistry
477: .getInstance().getFactory(type);
478: LoadStrategy loadStrategy = factory.create();
479: getModelItem().setLoadStrategy(loadStrategy);
480: loadStrategyConfigurationPanel.removeAll();
481: loadStrategyConfigurationPanel.add(loadStrategy
482: .getConfigurationPanel(), BorderLayout.CENTER);
483: loadStrategyConfigurationPanel.revalidate();
484: }
485:
486: private class InternalLoadTestListener extends
487: LoadTestRunListenerAdapter {
488: public void beforeLoadTest(LoadTestRunner testRunner,
489: LoadTestRunContext context) {
490: loadTestLog.clear();
491:
492: loadTestStartTime = System.currentTimeMillis();
493: loadTestIsRunning = true;
494: if (getModelItem().getTestLimit() > 0) {
495: progressBar.setValue(0);
496: progressBar.setString(null);
497: } else {
498: progressBar.setString("...");
499: }
500:
501: progressBar.setStringPainted(true);
502:
503: runButton.setEnabled(false);
504: cancelButton.setEnabled(true);
505: strategyCombo.setEnabled(false);
506: limitTypeCombo.setEnabled(false);
507: threadsSpinner.setEnabled(getModelItem().getLoadStrategy()
508: .allowThreadCountChangeDuringRun());
509:
510: new Thread(new ProgressBarUpdater(), getModelItem()
511: .getName()
512: + " ProgressBarUpdater").start();
513: }
514:
515: public void afterLoadTest(LoadTestRunner testRunner,
516: LoadTestRunContext context) {
517: runButton.setEnabled(true);
518:
519: cancelButton.setEnabled(false);
520: strategyCombo.setEnabled(true);
521: limitTypeCombo.setEnabled(true);
522: threadsSpinner.setEnabled(true);
523:
524: runner = null;
525: loadTestIsRunning = false;
526:
527: if (progressBar.isIndeterminate()) {
528: progressBar.setIndeterminate(false);
529: progressBar.setValue(0);
530: } else if (testRunner.getStatus() == Status.FINISHED) {
531: progressBar.setValue(100);
532: }
533:
534: if (testRunner.getStatus() == Status.FAILED) {
535: UISupport.showErrorMessage("LoadTest failed; "
536: + testRunner.getReason());
537: }
538: }
539:
540: }
541:
542: public String getDescription() {
543: return "LoadTest: [" + getModelItem().getName()
544: + "] for TestCase ["
545: + getModelItem().getTestCase().getName()
546: + "] in TestSuite ["
547: + getModelItem().getTestCase().getTestSuite().getName()
548: + "]";
549: }
550:
551: private class ProgressBarUpdater implements Runnable {
552: public void run() {
553: while (true) {
554: if (!loadTestIsRunning)
555: break;
556:
557: if (getModelItem().getTestLimit() == 0) {
558: if (loadTestIsRunning
559: && !progressBar.isIndeterminate()) {
560: progressBar.setIndeterminate(true);
561: progressBar.setString("...");
562: }
563: } else if (getModelItem().getLimitType() == LoadTestLimitTypesConfig.TIME) {
564: if (loadTestIsRunning
565: && progressBar.isIndeterminate()) {
566: progressBar.setIndeterminate(false);
567: progressBar.setString(null);
568: }
569:
570: long timePassed = System.currentTimeMillis()
571: - loadTestStartTime;
572: int value = (int) ((timePassed * 100) / (getModelItem()
573: .getTestLimit() * 1000));
574: progressBar.setValue(value);
575: } else if (getModelItem().getLimitType() == LoadTestLimitTypesConfig.COUNT) {
576: if (loadTestIsRunning
577: && progressBar.isIndeterminate()) {
578: progressBar.setIndeterminate(false);
579: progressBar.setString(null);
580: }
581:
582: long counts = getModelItem().getStatisticsModel()
583: .getStatistic(LoadTestStatistics.TOTAL,
584: Statistic.COUNT);
585: if (counts > 0)
586: progressBar
587: .setValue((int) ((counts * 100) / getModelItem()
588: .getTestLimit()));
589: }
590:
591: try {
592: Thread.sleep(500);
593: } catch (InterruptedException e) {
594: SoapUI.logError(e);
595: }
596: }
597: }
598: }
599:
600: public void propertyChange(PropertyChangeEvent evt) {
601: if (evt.getPropertyName().equals(
602: WsdlLoadTest.THREADCOUNT_PROPERTY)) {
603: threadsSpinner.setValue(evt.getNewValue());
604: } else if (evt.getPropertyName().equals(
605: WsdlLoadTest.HISTORYLIMIT_PROPERTY)) {
606: long lng = (Long) evt.getNewValue();
607:
608: statisticsGraphButton.setEnabled(lng != 0);
609: testTimesGraphButton.setEnabled(lng != 0);
610: }
611: }
612:
613: }
|