View Javadoc

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