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