View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2008 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.monitor;
14  
15  import com.eviware.soapui.SoapUI;
16  import com.eviware.soapui.impl.support.AbstractInterface;
17  import com.eviware.soapui.impl.wsdl.WsdlInterface;
18  import com.eviware.soapui.impl.wsdl.WsdlProject;
19  import com.eviware.soapui.impl.wsdl.WsdlRequest;
20  import com.eviware.soapui.impl.wsdl.WsdlTestSuite;
21  import com.eviware.soapui.impl.wsdl.mock.WsdlMockOperation;
22  import com.eviware.soapui.impl.wsdl.mock.WsdlMockResponse;
23  import com.eviware.soapui.impl.wsdl.mock.WsdlMockService;
24  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
25  import com.eviware.soapui.impl.wsdl.support.MessageExchangeModelItem;
26  import com.eviware.soapui.impl.wsdl.support.MessageExchangeRequestMessageEditor;
27  import com.eviware.soapui.impl.wsdl.support.MessageExchangeResponseMessageEditor;
28  import com.eviware.soapui.impl.wsdl.testcase.WsdlTestCase;
29  import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequest;
30  import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep;
31  import com.eviware.soapui.impl.wsdl.teststeps.registry.WsdlTestRequestStepFactory;
32  import com.eviware.soapui.model.iface.Attachment;
33  import com.eviware.soapui.model.iface.Interface;
34  import com.eviware.soapui.model.settings.Settings;
35  import com.eviware.soapui.model.support.ModelSupport;
36  import com.eviware.soapui.model.testsuite.TestSuite;
37  import com.eviware.soapui.settings.ProxySettings;
38  import com.eviware.soapui.support.StringUtils;
39  import com.eviware.soapui.support.UISupport;
40  import com.eviware.soapui.support.components.JComponentInspector;
41  import com.eviware.soapui.support.components.JInspectorPanel;
42  import com.eviware.soapui.support.components.JInspectorPanelFactory;
43  import com.eviware.soapui.support.components.JXToolBar;
44  import com.eviware.soapui.support.types.StringList;
45  import com.eviware.x.form.XFormDialog;
46  import com.eviware.x.form.XFormField;
47  import com.eviware.x.form.XFormFieldListener;
48  import com.eviware.x.form.support.ADialogBuilder;
49  import com.eviware.x.form.support.AField;
50  import com.eviware.x.form.support.AField.AFieldType;
51  import com.eviware.x.form.support.AForm;
52  import com.jgoodies.forms.builder.ButtonBarBuilder;
53  import org.jdesktop.swingx.JXTable;
54  import org.jdesktop.swingx.decorator.Filter;
55  import org.jdesktop.swingx.decorator.FilterPipeline;
56  import org.jdesktop.swingx.decorator.PatternFilter;
57  
58  import javax.swing.*;
59  import javax.swing.event.ListSelectionEvent;
60  import javax.swing.event.ListSelectionListener;
61  import javax.swing.table.AbstractTableModel;
62  import java.awt.*;
63  import java.awt.event.ActionEvent;
64  import java.awt.event.ActionListener;
65  import java.awt.event.ItemEvent;
66  import java.awt.event.ItemListener;
67  import java.net.MalformedURLException;
68  import java.net.URL;
69  import java.text.DateFormat;
70  import java.text.SimpleDateFormat;
71  import java.util.*;
72  import java.util.List;
73  
74  /***
75   * A SOAP Monitor..
76   */
77  
78  public class SoapMonitor extends JPanel
79  {
80     private static final String ALL_FILTER_OPTION = "- all -";
81     private JProgressBar progressBar;
82     private JButton stopButton = null;
83  
84     private JXTable logTable = null;
85     private MonitorLogTableModel tableModel = null;
86  
87     private String httpProxyHost = null;
88     private int httpProxyPort = 80;
89  
90     private JButton startButton;
91     private final WsdlProject project;
92     private MessageExchangeRequestMessageEditor requestViewer;
93     private MessageExchangeResponseMessageEditor responseViewer;
94     private Set<SoapMonitorListener> listeners = new HashSet<SoapMonitorListener>();
95     private MessageExchangeModelItem requestModelItem;
96     private JButton optionsButton;
97     private int listenPort;
98     private String targetEndpoint;
99     private JButton clearButton;
100    private int maxRows;
101    private JButton addToTestCaseButton;
102    private JButton createRequestButton;
103    private JButton addToMockServiceButton;
104    private Stack<WsdlMonitorMessageExchange> messageExchangeStack = new Stack<WsdlMonitorMessageExchange>();
105    private StackProcessor stackProcessor = new StackProcessor();
106    private PatternFilter operationFilter;
107    private PatternFilter interfaceFilter;
108    private PatternFilter targetHostFilter;
109    private JLabel infoLabel;
110    private PatternFilter requestHostFilter;
111    private JComboBox requestHostFilterCombo;
112    private JComboBox targetHostFilterCombo;
113    private JComboBox interfaceFilterCombo;
114    private JComboBox operationFilterCombo;
115    private DefaultComboBoxModel operationFilterModel;
116    private DefaultComboBoxModel requestFilterModel;
117    private DefaultComboBoxModel targetHostFilterModel;
118    private JLabel rowCountLabel = new JLabel();
119    private Map<AbstractInterface<?>, String> addedEndpoints;
120    private JXToolBar toolbar;
121    private String incomingRequestWss;
122    private String incomingResponseWss;
123    private boolean setAsProxy;
124    private XFormDialog optionsDialog;
125    private SoapMonitorEngine monitorEngine;
126    private String oldProxyHost;
127    private String oldProxyPort;
128 
129    public SoapMonitor(
130            WsdlProject project, int listenPort,
131            String incomingRequestWss, String incomingResponseWss, JXToolBar mainToolbar, boolean setAsProxy
132    )
133    {
134       super( new BorderLayout() );
135       this.project = project;
136       this.listenPort = listenPort;
137       this.incomingRequestWss = incomingRequestWss;
138       this.incomingResponseWss = incomingResponseWss;
139       this.setAsProxy = setAsProxy;
140       this.maxRows = 100;
141 
142       // set the slow link to the passed down link
143 
144       this.setLayout( new BorderLayout() );
145 
146       add( buildToolbars( mainToolbar ), BorderLayout.NORTH );
147       add( buildContent(), BorderLayout.CENTER );
148 
149       start();
150    }
151 
152    private JComponent buildContent()
153    {
154       JInspectorPanel inspectorPanel = JInspectorPanelFactory.build( buildLog() );
155 
156       JComponentInspector<JComponent> viewInspector = new JComponentInspector<JComponent>( buildViewer(), "Message Content",
157               "Shows message content", true );
158       inspectorPanel.addInspector( viewInspector );
159 
160       return inspectorPanel.getComponent();
161    }
162 
163    private JComponent buildLog()
164    {
165       tableModel = new MonitorLogTableModel();
166       logTable = new JXTable( 1, 2 );
167       logTable.setColumnControlVisible( true );
168       logTable.setModel( tableModel );
169       logTable.setHorizontalScrollEnabled( true );
170       logTable.setSelectionMode( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION );
171 
172       operationFilter = new PatternFilter( ".*", 0, 4 );
173       operationFilter.setAcceptNull( true );
174       interfaceFilter = new PatternFilter( ".*", 0, 3 );
175       interfaceFilter.setAcceptNull( true );
176       targetHostFilter = new PatternFilter( ".*", 0, 2 );
177       targetHostFilter.setAcceptNull( true );
178       requestHostFilter = new PatternFilter( ".*", 0, 1 );
179       requestHostFilter.setAcceptNull( true );
180 
181       Filter[] filters = new Filter[]{requestHostFilter, targetHostFilter, interfaceFilter, operationFilter};
182 
183       FilterPipeline pipeline = new FilterPipeline( filters );
184       logTable.setFilters( pipeline );
185 
186       ListSelectionModel sel = logTable.getSelectionModel();
187       sel.addListSelectionListener( new ListSelectionListener()
188       {
189          public void valueChanged( ListSelectionEvent event )
190          {
191             int row = logTable.getSelectedRow();
192             if( row == -1 )
193             {
194 //					requestXmlDocument.setXml( null );
195 //					responseXmlDocument.setXml( null );
196                requestModelItem.setMessageExchange( null );
197             }
198             else
199             {
200                WsdlMonitorMessageExchange exchange = tableModel.getMessageExchangeAt( row );
201                requestModelItem.setMessageExchange( exchange );
202                //		responseModelItem.setMessageExchange( exchange );
203 //					requestXmlDocument.setXml( exchange.getRequestContent() );
204 //					responseXmlDocument.setXml( exchange.getResponseContent() );
205             }
206 
207             addToMockServiceButton.setEnabled( row != -1 );
208             addToTestCaseButton.setEnabled( row != -1 );
209             createRequestButton.setEnabled( row != -1 );
210          }
211       } );
212 
213       JPanel tablePane = new JPanel();
214       tablePane.setLayout( new BorderLayout() );
215 
216       toolbar.addGlue();
217 
218       tablePane.add( buildFilterBar(), BorderLayout.NORTH );
219       tablePane.add( new JScrollPane( logTable ), BorderLayout.CENTER );
220 
221       return tablePane;
222    }
223 
224    private JPanel buildFilterBar()
225    {
226       requestFilterModel = new DefaultComboBoxModel( new String[]{ALL_FILTER_OPTION} );
227       targetHostFilterModel = new DefaultComboBoxModel( new String[]{ALL_FILTER_OPTION} );
228       Dimension comboBoxSize = new Dimension( 90, 18 );
229       requestHostFilterCombo = UISupport.setFixedSize( new JComboBox( requestFilterModel ), comboBoxSize );
230 
231 //		toolbar.addFixed( new JLabel( "<html><b>Filter:</b></html>"));
232 //		toolbar.addUnrelatedGap();
233 
234       ButtonBarBuilder toolbar = new ButtonBarBuilder();
235 
236       toolbar.addFixed( new JLabel( "Request Host" ) );
237       toolbar.addRelatedGap();
238       toolbar.addFixed( requestHostFilterCombo );
239       toolbar.addUnrelatedGap();
240 
241       requestHostFilterCombo.addItemListener( new ItemListener()
242       {
243          public void itemStateChanged( ItemEvent e )
244          {
245             int ix = requestHostFilterCombo.getSelectedIndex();
246             if( ix == -1 )
247                return;
248 
249             requestHostFilter.setAcceptNull( ix == 0 );
250 
251             if( ix == 0 )
252                requestHostFilter.setPattern( ".*", 0 );
253             else
254                requestHostFilter.setPattern( requestHostFilterCombo.getSelectedItem().toString(), 0 );
255 
256             updateRowCountLabel();
257          }
258       } );
259 
260       toolbar.addFixed( new JLabel( "Target Host" ) );
261       toolbar.addRelatedGap();
262       targetHostFilterCombo = UISupport.setFixedSize( new JComboBox( targetHostFilterModel ), comboBoxSize );
263       toolbar.addFixed( targetHostFilterCombo );
264       toolbar.addUnrelatedGap();
265 
266       targetHostFilterCombo.addItemListener( new ItemListener()
267       {
268          public void itemStateChanged( ItemEvent e )
269          {
270             int ix = targetHostFilterCombo.getSelectedIndex();
271             if( ix == -1 )
272                return;
273 
274             targetHostFilter.setAcceptNull( ix == 0 );
275 
276             if( ix == 0 )
277                targetHostFilter.setPattern( ".*", 0 );
278             else
279                targetHostFilter.setPattern( targetHostFilterCombo.getSelectedItem().toString(), 0 );
280 
281             updateRowCountLabel();
282          }
283       } );
284 
285       String[] interfaceNames = ModelSupport
286               .getNames( new String[]{ALL_FILTER_OPTION}, getProject().getInterfaceList() );
287 
288       toolbar.addFixed( new JLabel( "Interface" ) );
289       toolbar.addRelatedGap();
290       interfaceFilterCombo = UISupport.setFixedSize( new JComboBox( interfaceNames ), comboBoxSize );
291       toolbar.addFixed( interfaceFilterCombo );
292       toolbar.addUnrelatedGap();
293 
294       operationFilterModel = new DefaultComboBoxModel( new String[]{ALL_FILTER_OPTION} );
295       interfaceFilterCombo.addItemListener( new ItemListener()
296       {
297          public void itemStateChanged( ItemEvent e )
298          {
299             String item = (String) interfaceFilterCombo.getSelectedItem();
300             operationFilterModel.removeAllElements();
301 
302             if( item == null || getProject().getInterfaceByName( item ) == null )
303             {
304                operationFilterModel.addElement( ALL_FILTER_OPTION );
305                interfaceFilter.setPattern( ".*", 0 );
306             }
307             else if( getProject().getInterfaceByName( item ) != null )
308             {
309                WsdlInterface iface = (WsdlInterface) getProject().getInterfaceByName( item );
310                String[] operationNames = ModelSupport.getNames( new String[]{ALL_FILTER_OPTION}, iface
311                        .getOperationList() );
312                for( String s : operationNames )
313                   operationFilterModel.addElement( s );
314 
315                interfaceFilter.setPattern( iface.getName(), 0 );
316             }
317          }
318       } );
319 
320       toolbar.addFixed( new JLabel( "Operation" ) );
321       toolbar.addRelatedGap();
322       operationFilterCombo = UISupport.setFixedSize( new JComboBox( operationFilterModel ), comboBoxSize );
323       toolbar.addFixed( operationFilterCombo );
324 
325       operationFilterCombo.addItemListener( new ItemListener()
326       {
327          public void itemStateChanged( ItemEvent e )
328          {
329             int ix = operationFilterCombo.getSelectedIndex();
330             if( ix == -1 )
331             {
332                operationFilter.setPattern( ".*", 0 );
333                updateRowCountLabel();
334                return;
335             }
336 
337             operationFilter.setAcceptNull( ix == 0 );
338 
339             if( ix == 0 )
340                operationFilter.setPattern( ".*", 0 );
341             else
342                operationFilter.setPattern( operationFilterCombo.getSelectedItem().toString(), 0 );
343 
344             updateRowCountLabel();
345          }
346       } );
347 
348       toolbar.setBorder( BorderFactory.createEmptyBorder( 3, 2, 3, 0 ) );
349       return toolbar.getPanel();
350    }
351 
352    protected void updateRowCountLabel()
353    {
354       rowCountLabel.setText( logTable.getRowCount() + "/" + tableModel.getRowCount() + " entries" );
355    }
356 
357    private JComponent buildViewer()
358    {
359       requestModelItem = new MessageExchangeModelItem( "monitor message exchange", null )
360       {
361 
362          @Override
363          public boolean hasRawData()
364          {
365             return true;
366          }
367       };
368 
369       requestViewer = new MessageExchangeRequestMessageEditor( requestModelItem );
370       responseViewer = new MessageExchangeResponseMessageEditor( requestModelItem );
371 
372       return UISupport.createHorizontalSplit( requestViewer, responseViewer );
373    }
374 
375    private JComponent buildToolbars( JXToolBar mainToolbar )
376    {
377       toolbar = UISupport.createSmallToolbar();
378       mainToolbar.addFixed( startButton = UISupport.createToolbarButton( UISupport.createImageIcon( "/run_testcase.gif" ) ) );
379       mainToolbar.addFixed( stopButton = UISupport.createToolbarButton( UISupport.createImageIcon( "/stop_testcase.gif" ) ) );
380       mainToolbar.addFixed( optionsButton = UISupport.createToolbarButton( new SoapMonitorOptionsAction() ) );
381 
382       toolbar.addFixed( createRequestButton = UISupport.createToolbarButton( UISupport.createImageIcon( "/request.gif" ) ) );
383       toolbar.addFixed( addToTestCaseButton = UISupport.createToolbarButton( UISupport.createImageIcon( "/testCase.gif" ) ) );
384       toolbar.addFixed( addToMockServiceButton = UISupport.createToolbarButton( UISupport.createImageIcon( "/mockService.gif" ) ) );
385       toolbar.addFixed( clearButton = UISupport.createToolbarButton( UISupport.createImageIcon( "/clear_loadtest.gif" ) ) );
386 
387       startButton.setToolTipText( "Starts the SOAP Monitor as configured" );
388       stopButton.setToolTipText( "Stops the SOAP Monitor" );
389       optionsButton.setToolTipText( "Sets Monitor Options" );
390       clearButton.setToolTipText( "Clear all/selected messages from the log" );
391       createRequestButton.setToolTipText( "Creates requests from selected messages" );
392       addToTestCaseButton.setToolTipText( "Adds selected requests to a TestCase" );
393       addToMockServiceButton.setToolTipText( "Adds selected reponses to a MockService" );
394 
395       createRequestButton.setEnabled( false );
396       addToMockServiceButton.setEnabled( false );
397       addToTestCaseButton.setEnabled( false );
398 
399       startButton.addActionListener( new ActionListener()
400       {
401          public void actionPerformed( ActionEvent e )
402          {
403             start();
404          }
405       } );
406 
407       stopButton.addActionListener( new ActionListener()
408       {
409          public void actionPerformed( ActionEvent e )
410          {
411             stop();
412          }
413       } );
414 
415       clearButton.addActionListener( new ClearAction() );
416       createRequestButton.addActionListener( new CreateRequestsAction() );
417       addToTestCaseButton.addActionListener( new AddToTestCaseAction() );
418       addToMockServiceButton.addActionListener( new AddToMockServiceAction() );
419 
420       mainToolbar.addGlue();
421 
422       infoLabel = new JLabel();
423       infoLabel.setPreferredSize( new Dimension( 100, 20 ) );
424       infoLabel.setOpaque( false );
425       mainToolbar.addFixed( infoLabel );
426 
427       progressBar = new JProgressBar();
428 
429       JPanel progressBarPanel = UISupport.createProgressBarPanel( progressBar, 2, false );
430       progressBarPanel.setPreferredSize( new Dimension( 60, 20 ) );
431 
432       mainToolbar.addFixed( progressBarPanel );
433       return toolbar;
434    }
435 
436    /***
437     * Method start
438     */
439    public void start()
440    {
441       int localPort = getLocalPort();
442 //		monitorEngine = new TcpMonMonitorEngine();
443 
444       monitorEngine = new SoapMonitorEngineImpl();
445       monitorEngine.start( this, localPort );
446 
447       if( monitorEngine.isRunning() )
448       {
449          stopButton.setEnabled( true );
450          startButton.setEnabled( false );
451          optionsButton.setEnabled( false );
452          infoLabel.setText( "Proxy " + "on port " + localPort );
453          progressBar.setIndeterminate( true );
454 
455          if( setAsProxy )
456          {
457             oldProxyHost = SoapUI.getSettings().getString( ProxySettings.HOST, "" );
458             oldProxyPort = SoapUI.getSettings().getString( ProxySettings.PORT, "" );
459 
460             SoapUI.getSettings().setString( ProxySettings.HOST, "127.0.0.1");
461             SoapUI.getSettings().setString( ProxySettings.PORT, String.valueOf(localPort) );
462          }
463 
464          SoapUI.log.info( "Started SOAP Monitor on local port " + localPort );
465       }
466       else
467       {
468          stopButton.setEnabled( false );
469          startButton.setEnabled( true );
470          optionsButton.setEnabled( true );
471          infoLabel.setText( "Stoped" );
472          progressBar.setIndeterminate( false );
473 
474          SoapUI.log.info( "Could not start SOAP Monitor on local port " + localPort );
475       }
476    }
477 
478    /***
479     * Method close
480     */
481    public void close()
482    {
483       stop();
484    }
485 
486    /***
487     * Method stop
488     */
489    public void stop()
490    {
491       monitorEngine.stop();
492       if( addedEndpoints != null )
493       {
494          for( Interface iface : addedEndpoints.keySet() )
495             iface.removeEndpoint( addedEndpoints.get( iface ) );
496 
497          addedEndpoints.clear();
498       }
499 
500       stopButton.setEnabled( false );
501       startButton.setEnabled( true );
502       optionsButton.setEnabled( true );
503       progressBar.setIndeterminate( false );
504       infoLabel.setText( "Stopped" );
505 
506       if( setAsProxy )
507       {
508          SoapUI.getSettings().setString( ProxySettings.HOST, oldProxyHost);
509          SoapUI.getSettings().setString( ProxySettings.PORT, oldProxyPort );
510       }
511    }
512 
513    @AForm( description = "Set options for adding selected requests to a MockService", name = "Add To MockService" )
514    private final class AddToMockServiceAction implements ActionListener
515    {
516       private static final String CREATE_NEW_OPTION = "<Create New>";
517       private XFormDialog dialog;
518 
519       @AField( name = "Target MockService", description = "The target TestSuite", type = AFieldType.ENUMERATION )
520       public final static String MOCKSERVICE = "Target MockService";
521 
522       @AField( name = "Open Editor", description = "Open the created MockService", type = AFieldType.BOOLEAN )
523       public final static String OPENEDITOR = "Open Editor";
524 
525       public void actionPerformed( ActionEvent e )
526       {
527          int[] rows = logTable.getSelectedRows();
528          if( rows.length == 0 )
529             return;
530 
531          if( dialog == null )
532          {
533             dialog = ADialogBuilder.buildDialog( this.getClass() );
534          }
535 
536          String[] testSuiteNames = ModelSupport.getNames( new String[]{CREATE_NEW_OPTION}, getProject()
537                  .getMockServiceList() );
538          dialog.setOptions( MOCKSERVICE, testSuiteNames );
539 
540          if( dialog.show() )
541          {
542             String targetMockServiceName = dialog.getValue( MOCKSERVICE );
543 
544             WsdlMockService mockService = getProject().getMockServiceByName( targetMockServiceName );
545             if( mockService == null )
546             {
547                targetMockServiceName = ModelSupport.promptForUniqueName( "MockService", getProject(), "" );
548                if( targetMockServiceName == null )
549                   return;
550 
551                mockService = getProject().addNewMockService( targetMockServiceName );
552                mockService.setIncomingWss( incomingResponseWss );
553             }
554 
555             int cnt = 0;
556             for( int row : rows )
557             {
558                WsdlMonitorMessageExchange me = tableModel.getMessageExchangeAt( row );
559                if( me.getOperation() == null )
560                   continue;
561 
562                WsdlMockOperation mockOperation = mockService.getMockOperation( me.getOperation() );
563                if( mockOperation == null )
564                   mockOperation = mockService.addNewMockOperation( me.getOperation() );
565 
566                WsdlMockResponse mockResponse = mockOperation.addNewMockResponse( "Monitor Response " + ( ++cnt ), false );
567                mockResponse.setResponseContent( me.getResponseContent() );
568 
569                Attachment[] requestAttachments = me.getResponseAttachments();
570                if( requestAttachments != null )
571                {
572                   for( Attachment attachment : requestAttachments )
573                   {
574                      mockResponse.addAttachment( attachment );
575                   }
576                }
577             }
578 
579             if( cnt == 0 )
580             {
581                UISupport.showInfoMessage( "No response messages found" );
582             }
583             else
584             {
585                UISupport.showInfoMessage( "Added " + cnt + " MockResponses to MockService" );
586 
587                if( dialog.getBooleanValue( OPENEDITOR ) )
588                   UISupport.selectAndShow( mockService );
589             }
590          }
591       }
592    }
593 
594    @AForm( description = "Set options for adding selected requests to a TestCase", name = "Add To TestCase" )
595    private final class AddToTestCaseAction implements ActionListener
596    {
597       private static final String CREATE_NEW_OPTION = "<Create New>";
598       private XFormDialog dialog;
599 
600       @AField( name = "Target TestCase", description = "The target TestCase for the requests", type = AFieldType.ENUMERATION )
601       public final static String TESTCASE = "Target TestCase";
602 
603       @AField( name = "Target TestSuite", description = "The target TestSuite", type = AFieldType.ENUMERATION )
604       public final static String TESTSUITE = "Target TestSuite";
605 
606       @AField( name = "Open Editor", description = "Open the created TestCase", type = AFieldType.BOOLEAN )
607       public final static String OPENEDITOR = "Open Editor";
608 
609       public void actionPerformed( ActionEvent e )
610       {
611          int[] rows = logTable.getSelectedRows();
612          if( rows.length == 0 )
613             return;
614 
615          if( dialog == null )
616          {
617             dialog = ADialogBuilder.buildDialog( this.getClass() );
618             dialog.getFormField( TESTSUITE ).addFormFieldListener( new XFormFieldListener()
619             {
620                public void valueChanged( XFormField sourceField, String newValue, String oldValue )
621                {
622                   if( newValue.equals( CREATE_NEW_OPTION ) )
623                   {
624                      dialog.setOptions( TESTCASE, new String[]{CREATE_NEW_OPTION} );
625                   }
626                   else
627                   {
628                      TestSuite testSuite = getProject().getTestSuiteByName( newValue );
629                      dialog.setOptions( TESTCASE, testSuite == null ? new String[]{CREATE_NEW_OPTION} : ModelSupport
630                              .getNames( testSuite.getTestCaseList(), new String[]{CREATE_NEW_OPTION} ) );
631                   }
632                }
633             } );
634          }
635 
636          String[] testSuiteNames = ModelSupport.getNames( new String[]{CREATE_NEW_OPTION}, getProject()
637                  .getTestSuiteList() );
638          dialog.setOptions( TESTSUITE, testSuiteNames );
639          dialog.setOptions( TESTCASE, new String[]{CREATE_NEW_OPTION} );
640 
641          if( dialog.show() )
642          {
643             String targetTestSuiteName = dialog.getValue( TESTSUITE );
644             String targetTestCaseName = dialog.getValue( TESTCASE );
645 
646             WsdlTestSuite testSuite = getProject().getTestSuiteByName( targetTestSuiteName );
647             if( testSuite == null )
648             {
649                targetTestSuiteName = ModelSupport.promptForUniqueName( "TestSuite", getProject(), "" );
650                if( targetTestSuiteName == null )
651                   return;
652 
653                testSuite = getProject().addNewTestSuite( targetTestSuiteName );
654             }
655 
656             WsdlTestCase testCase = testSuite.getTestCaseByName( targetTestCaseName );
657             if( testCase == null )
658             {
659                targetTestCaseName = ModelSupport.promptForUniqueName( "TestCase", testSuite, "" );
660                if( targetTestCaseName == null )
661                   return;
662 
663                testCase = testSuite.addNewTestCase( targetTestCaseName );
664             }
665 
666             for( int row : rows )
667             {
668                WsdlMonitorMessageExchange me = tableModel.getMessageExchangeAt( row );
669                if( me.getOperation() == null )
670                   continue;
671 
672                WsdlTestRequestStep test = (WsdlTestRequestStep) testCase.insertTestStep( WsdlTestRequestStepFactory
673                        .createConfig( me.getOperation(), "Monitor Request " + ( row + 1 ) ), -1 );
674 
675                WsdlTestRequest request = test.getTestRequest();
676                request.setRequestContent( me.getRequestContent() );
677                request.setEndpoint( me.getTargetUrl().toString() );
678                request.setIncomingWss( incomingRequestWss );
679 
680                Attachment[] requestAttachments = me.getRequestAttachments();
681                if( requestAttachments != null )
682                {
683                   for( Attachment attachment : requestAttachments )
684                   {
685                      request.importAttachment( attachment );
686                   }
687                }
688             }
689 
690             if( dialog.getBooleanValue( OPENEDITOR ) )
691                UISupport.selectAndShow( testCase );
692          }
693       }
694    }
695 
696    private final class CreateRequestsAction implements ActionListener
697    {
698       public void actionPerformed( ActionEvent e )
699       {
700          int[] rows = logTable.getSelectedRows();
701          if( rows.length == 0 )
702             return;
703 
704          if( UISupport.confirm( "Create " + rows.length + " requests", "Create Request" ) )
705          {
706             for( int row : rows )
707             {
708                WsdlMonitorMessageExchange me = tableModel.getMessageExchangeAt( row );
709                if( me.getOperation() == null )
710                   continue;
711 
712                WsdlRequest request = me.getOperation().addNewRequest( "Monitor Request " + ( row + 1 ) );
713 
714                request.setRequestContent( me.getRequestContent() );
715                request.setEndpoint( me.getTargetUrl().toString() );
716 
717                Attachment[] requestAttachments = me.getRequestAttachments();
718                if( requestAttachments != null )
719                {
720                   for( Attachment attachment : requestAttachments )
721                   {
722                      request.importAttachment( attachment );
723                   }
724                }
725             }
726          }
727       }
728    }
729 
730    private final class ClearAction implements ActionListener
731    {
732       public void actionPerformed( ActionEvent e )
733       {
734          if( logTable.getRowCount() == 0 )
735             return;
736 
737          int[] rows = logTable.getSelectedRows();
738 
739          if( rows.length == 0 )
740          {
741             if( UISupport.confirm( "Clear monitor log?", "Clear Log" ) )
742                tableModel.clear();
743          }
744          else if( UISupport.confirm( "Clear " + rows.length + " rows from monitor log?", "Clear Log" ) )
745          {
746             tableModel.clearRows( rows );
747          }
748       }
749    }
750 
751    public class MonitorLogTableModel extends AbstractTableModel
752    {
753       private List<WsdlMonitorMessageExchange> exchanges = new LinkedList<WsdlMonitorMessageExchange>();
754       private DateFormat sdf;
755 
756       public MonitorLogTableModel()
757       {
758          sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
759       }
760 
761       public synchronized void clear()
762       {
763          int sz = exchanges.size();
764          while( exchanges.size() > 0 )
765          {
766             WsdlMonitorMessageExchange removed = exchanges.remove( 0 );
767             removed.discard();
768          }
769 
770          fireTableRowsDeleted( 0, sz );
771 
772          while( requestFilterModel.getSize() > 1 )
773             requestFilterModel.removeElementAt( 1 );
774 
775          while( targetHostFilterModel.getSize() > 1 )
776             targetHostFilterModel.removeElementAt( 1 );
777 
778          updateRowCountLabel();
779       }
780 
781       public synchronized void clearRows( int[] indices )
782       {
783          for( int c = indices.length; c > 0; c-- )
784          {
785             int index = indices[c - 1];
786             WsdlMonitorMessageExchange removed = exchanges.remove( logTable.convertRowIndexToModel( index ) );
787             removed.discard();
788             fireTableRowsDeleted( index, index );
789             updateRowCountLabel();
790          }
791       }
792 
793       public int getColumnCount()
794       {
795          return 8;
796       }
797 
798       public WsdlMonitorMessageExchange getMessageExchangeAt( int tableRow )
799       {
800          return exchanges.get( logTable.convertRowIndexToModel( tableRow ) );
801       }
802 
803       @Override
804       public String getColumnName( int column )
805       {
806          switch( column )
807          {
808             case 0:
809                return "Time";
810             case 1:
811                return "Request Host";
812             case 2:
813                return "Target Host";
814             case 3:
815                return "Interface";
816             case 4:
817                return "Operation";
818             case 5:
819                return "Time Taken";
820             case 6:
821                return "Req Sz";
822             case 7:
823                return "Resp Sz";
824          }
825 
826          return null;
827       }
828 
829       public int getRowCount()
830       {
831          return exchanges.size();
832       }
833 
834       public Object getValueAt( int rowIndex, int columnIndex )
835       {
836          if( rowIndex < 0 || rowIndex >= exchanges.size() )
837             return null;
838 
839          WsdlMonitorMessageExchange exchange = exchanges.get( rowIndex );
840          if( exchange == null )
841             return null;
842 
843          switch( columnIndex )
844          {
845             case 0:
846                return sdf.format( new Date( exchange.getTimestamp() ) );
847             case 1:
848                return exchange.getRequestHost();
849             case 2:
850                return exchange.getTargetUrl().getHost();
851             case 3:
852                return exchange.getOperation() == null ? "- unknown -" : exchange.getOperation().getInterface().getName();
853             case 4:
854                return exchange.getOperation() == null ? "- unknown -" : exchange.getOperation().getName();
855             case 5:
856                return String.valueOf( exchange.getTimeTaken() );
857             case 6:
858                return String.valueOf( exchange.getRequestContentLength() );
859             case 7:
860                return String.valueOf( exchange.getResponseContentLength() );
861          }
862 
863          return null;
864       }
865 
866       public synchronized void addMessageExchange( WsdlMonitorMessageExchange exchange )
867       {
868          exchanges.add( exchange );
869          int size = exchanges.size();
870          fireTableRowsInserted( size - 1, size );
871 
872          fitSizeToMaxRows();
873 
874          String requestHost = exchange.getRequestHost();
875          if( requestFilterModel.getIndexOf( requestHost ) == -1 )
876          {
877             requestFilterModel.addElement( requestHost );
878          }
879 
880          String host = exchange.getTargetUrl().getHost();
881          if( targetHostFilterModel.getIndexOf( host ) == -1 )
882          {
883             targetHostFilterModel.addElement( host );
884          }
885 
886          updateRowCountLabel();
887       }
888 
889       public void fitSizeToMaxRows()
890       {
891          int removeCnt = 0;
892 
893          while( exchanges.size() > maxRows )
894          {
895             WsdlMonitorMessageExchange removed = exchanges.remove( 0 );
896             removed.discard();
897             removeCnt++;
898          }
899 
900          if( removeCnt > 0 )
901          {
902             fireTableDataChanged();
903             updateRowCountLabel();
904          }
905       }
906    }
907 
908    protected String getHttpProxyHost()
909    {
910       return httpProxyHost;
911    }
912 
913    protected void setHttpProxyHost( String proxyHost )
914    {
915       httpProxyHost = proxyHost;
916    }
917 
918    protected int getHttpProxyPort()
919    {
920       return httpProxyPort;
921    }
922 
923    protected void setHttpProxyPort( int proxyPort )
924    {
925       httpProxyPort = proxyPort;
926    }
927 
928 //	protected SlowLinkSimulator getSlowLink()
929 //	{
930 //		return slowLink;
931 //	}
932 
933    public String getTargetHost()
934    {
935       String host = targetEndpoint;
936 
937       try
938       {
939          URL url = new URL( host );
940          return url.getHost();
941       }
942       catch( MalformedURLException e )
943       {
944          return host;
945       }
946    }
947 
948    public String getTargetEndpoint()
949    {
950       return targetEndpoint;
951    }
952 
953    public int getTargetPort()
954    {
955       try
956       {
957          URL url = new URL( targetEndpoint );
958          return url.getPort() == -1 ? 80 : url.getPort();
959       }
960       catch( MalformedURLException e )
961       {
962          return 80;
963       }
964    }
965 
966    public int getLocalPort()
967    {
968       return listenPort;
969    }
970 
971    public synchronized void addMessageExchange( WsdlMonitorMessageExchange messageExchange )
972    {
973       messageExchangeStack.push( messageExchange );
974 
975       if( !stackProcessor.isRunning() )
976          new Thread( stackProcessor, "SoapMonitor StackProcessor for project [" + getProject().getName() + "]" )
977                  .start();
978    }
979 
980    private class StackProcessor implements Runnable
981    {
982       private boolean canceled;
983       private boolean running;
984 
985       public void run()
986       {
987          running = true;
988          SoapUI.log.info( "Started stackprocessor for soapmonitor in project [" + getProject().getName() + "]" );
989          while( !canceled && messageExchangeStack.size() > 0 )
990          {
991             WsdlMonitorMessageExchange messageExchange = messageExchangeStack.pop();
992             if( messageExchange != null )
993             {
994                processMessage( messageExchange );
995             }
996 
997             try
998             {
999                Thread.sleep( 100 );
1000             }
1001             catch( InterruptedException e )
1002             {
1003                e.printStackTrace();
1004             }
1005          }
1006          running = false;
1007       }
1008 
1009       private synchronized void processMessage( WsdlMonitorMessageExchange messageExchange )
1010       {
1011          messageExchange.prepare(
1012                  project.getWssContainer().getIncomingWssByName( incomingRequestWss ),
1013                  project.getWssContainer().getIncomingWssByName( incomingResponseWss ) );
1014 
1015          tableModel.addMessageExchange( messageExchange );
1016          if( logTable.getSelectedRow() == logTable.getRowCount() - 2 )
1017          {
1018             logTable.setRowSelectionInterval( logTable.getRowCount() - 1, logTable.getRowCount() - 1 );
1019          }
1020 
1021          fireOnMessageExchange( messageExchange );
1022       }
1023 
1024       public void cancel()
1025       {
1026          canceled = true;
1027       }
1028 
1029       protected boolean isCanceled()
1030       {
1031          return canceled;
1032       }
1033 
1034       protected boolean isRunning()
1035       {
1036          return running;
1037       }
1038    }
1039 
1040    public MonitorLogTableModel getLogModel()
1041    {
1042       return tableModel;
1043    }
1044 
1045    public void addSoapMonitorListener( SoapMonitorListener listener )
1046    {
1047       listeners.add( listener );
1048    }
1049 
1050    public void fireOnMessageExchange( WsdlMonitorMessageExchange messageExchange )
1051    {
1052       for( SoapMonitorListener listener : listeners.toArray( new SoapMonitorListener[listeners.size()] ) )
1053       {
1054          listener.onMessageExchange( messageExchange );
1055       }
1056    }
1057 
1058    public void removeSoapMonitorListener( SoapMonitorListener listener )
1059    {
1060       listeners.remove( listener );
1061    }
1062 
1063    public WsdlProject getProject()
1064    {
1065       return project;
1066    }
1067 
1068    public class SoapMonitorOptionsAction extends AbstractAction
1069    {
1070 
1071       public SoapMonitorOptionsAction()
1072       {
1073          putValue( SMALL_ICON, UISupport.createImageIcon( "/options.gif" ) );
1074       }
1075 
1076       public void actionPerformed( ActionEvent e )
1077       {
1078          if( optionsDialog == null )
1079          {
1080             optionsDialog = ADialogBuilder.buildDialog( OptionsForm.class );
1081          }
1082 
1083          StringList endpoints = new StringList();
1084          endpoints.add( null );
1085 
1086          for( Interface iface : getProject().getInterfaceList() )
1087          {
1088             endpoints.addAll( iface.getEndpoints() );
1089          }
1090 
1091          optionsDialog.setIntValue( OptionsForm.PORT, listenPort );
1092          optionsDialog.setIntValue( OptionsForm.MAXROWS, maxRows );
1093 
1094          optionsDialog.setOptions( OptionsForm.REQUEST_WSS,
1095                  StringUtils.merge( project.getWssContainer().getIncomingWssNames(), "<none>" ) );
1096          optionsDialog.setOptions( OptionsForm.RESPONSE_WSS,
1097                  StringUtils.merge( project.getWssContainer().getIncomingWssNames(), "<none>" ) );
1098 
1099          optionsDialog.setValue( OptionsForm.REQUEST_WSS, incomingRequestWss );
1100          optionsDialog.setValue( OptionsForm.RESPONSE_WSS, incomingResponseWss );
1101 
1102          if( optionsDialog.show() )
1103          {
1104             Settings settings = getProject().getSettings();
1105 
1106             settings.setLong( OptionsForm.PORT, listenPort = optionsDialog.getIntValue( OptionsForm.PORT, listenPort ) );
1107             settings.setLong( OptionsForm.MAXROWS, maxRows = optionsDialog.getIntValue( OptionsForm.MAXROWS, maxRows ) );
1108 
1109             incomingRequestWss = optionsDialog.getValue( OptionsForm.REQUEST_WSS );
1110             incomingResponseWss = optionsDialog.getValue( OptionsForm.RESPONSE_WSS );
1111 
1112             tableModel.fitSizeToMaxRows();
1113          }
1114       }
1115 
1116       @AForm( name = "SOAP Monitor Options", description = "Set options for SOAP Monitor", helpUrl = HelpUrls.SOAPMONITOR_HELP_URL, icon = UISupport.OPTIONS_ICON_PATH )
1117       private class OptionsForm
1118       {
1119          @AField( description = "The local port to listen on", name = "Port", type = AFieldType.INT )
1120          public final static String PORT = "Port";
1121 
1122          @AField( description = "The maximum number of exchanges to log", name = "Max Log", type = AFieldType.INT )
1123          public final static String MAXROWS = "Max Log";
1124 
1125          @AField( description = "The Incoming WSS configuration to use for processing requests", name = "Incoming Request WSS", type = AFieldType.ENUMERATION )
1126          public final static String REQUEST_WSS = "Incoming Request WSS";
1127 
1128          @AField( description = "The Outgoing WSS configuration to use for processing responses", name = "Incoming Response WSS", type = AFieldType.ENUMERATION )
1129          public final static String RESPONSE_WSS = "Incoming Response WSS";
1130       }
1131    }
1132 
1133    public void release()
1134    {
1135       requestViewer.release();
1136       responseViewer.release();
1137 
1138       if( optionsDialog != null )
1139       {
1140          optionsDialog.release();
1141          optionsDialog = null;
1142       }
1143    }
1144 
1145    public boolean isRunning()
1146    {
1147       return monitorEngine.isRunning();
1148    }
1149 }