View Javadoc

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