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( Exception 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 				}
610 
611 				int cnt = 0;
612 				for( int row : rows )
613 				{
614 					WsdlMonitorMessageExchange me = tableModel.getMessageExchangeAt( row );
615 
616 					WsdlMockOperation mockOperation = mockService.getMockOperation( me.getOperation() );
617 					if( mockOperation == null )
618 						mockOperation = mockService.addNewMockOperation( me.getOperation() );
619 
620 					WsdlMockResponse mockResponse = mockOperation.addNewMockResponse( "Monitor Response " + ( ++cnt ), false );
621 					mockResponse.setResponseContent( me.getResponseContent() );
622 
623 					Attachment[] requestAttachments = me.getResponseAttachments();
624 					if( requestAttachments != null )
625 					{
626 						for( Attachment attachment : requestAttachments )
627 						{
628 							mockResponse.addAttachment( attachment );
629 						}
630 					}
631 				}
632 				
633 				if( cnt == 0 )
634 				{
635 					UISupport.showInfoMessage( "No response messages found" );
636 				}
637 				else
638 				{
639 					UISupport.showInfoMessage( "Added " + cnt + " MockResponses to MockService" );
640 					
641 					if( dialog.getBooleanValue( OPENEDITOR ))
642 						UISupport.selectAndShow( mockService );
643 				}
644 			}
645 		}
646 	}
647 
648 	@AForm( description = "Set options for adding selected requests to a TestCase", name = "Add To TestCase" )
649 	private final class AddToTestCaseAction implements ActionListener
650 	{
651 		private static final String CREATE_NEW_OPTION = "<Create New>";
652 		private XFormDialog dialog;
653 
654 		@AField( name = "Target TestCase", description = "The target TestCase for the requests", type = AFieldType.ENUMERATION )
655 		public final static String TESTCASE = "Target TestCase";
656 
657 		@AField( name = "Target TestSuite", description = "The target TestSuite", type = AFieldType.ENUMERATION )
658 		public final static String TESTSUITE = "Target TestSuite";
659 
660 		@AField( name = "Open Editor", description = "Open the created TestCase", type = AFieldType.BOOLEAN )
661 		public final static String OPENEDITOR = "Open Editor";
662 		
663 		public void actionPerformed( ActionEvent e )
664 		{
665 			int[] rows = logTable.getSelectedRows();
666 			if( rows.length == 0 )
667 				return;
668 
669 			if( dialog == null )
670 			{
671 				dialog = ADialogBuilder.buildDialog( this.getClass() );
672 				dialog.getFormField( TESTSUITE ).addFormFieldListener( new XFormFieldListener()
673 				{
674 					public void valueChanged( XFormField sourceField, String newValue, String oldValue )
675 					{
676 						if( newValue.equals( CREATE_NEW_OPTION ) )
677 						{
678 							dialog.setOptions( TESTCASE, new String[] { CREATE_NEW_OPTION } );
679 						}
680 						else
681 						{
682 							TestSuite testSuite = getProject().getTestSuiteByName( newValue );
683 							dialog.setOptions( TESTCASE, testSuite == null ? new String[] { CREATE_NEW_OPTION } : ModelSupport
684 										.getNames( testSuite.getTestCaseList(), new String[] { CREATE_NEW_OPTION } ) );
685 						}
686 					}
687 				} );
688 			}
689 
690 			String[] testSuiteNames = ModelSupport.getNames( new String[] { CREATE_NEW_OPTION }, getProject()
691 						.getTestSuiteList() );
692 			dialog.setOptions( TESTSUITE, testSuiteNames );
693 			dialog.setOptions( TESTCASE, new String[] { CREATE_NEW_OPTION } );
694 
695 			if( dialog.show() )
696 			{
697 				String targetTestSuiteName = dialog.getValue( TESTSUITE );
698 				String targetTestCaseName = dialog.getValue( TESTCASE );
699 
700 				WsdlTestSuite testSuite = getProject().getTestSuiteByName( targetTestSuiteName );
701 				if( testSuite == null )
702 				{
703 					targetTestSuiteName = ModelSupport.promptForUniqueName( "TestSuite", getProject(), "" );
704 					if( targetTestSuiteName == null )
705 						return;
706 
707 					testSuite = getProject().addNewTestSuite( targetTestSuiteName );
708 				}
709 
710 				WsdlTestCase testCase = testSuite.getTestCaseByName( targetTestCaseName );
711 				if( testCase == null )
712 				{
713 					targetTestCaseName = ModelSupport.promptForUniqueName( "TestCase", testSuite, "" );
714 					if( targetTestCaseName == null )
715 						return;
716 
717 					testCase = testSuite.addNewTestCase( targetTestCaseName );
718 				}
719 
720 				for( int row : rows )
721 				{
722 					WsdlMonitorMessageExchange me = tableModel.getMessageExchangeAt( row );
723 
724 					WsdlTestRequestStep test = ( WsdlTestRequestStep ) testCase.insertTestStep( WsdlTestRequestStepFactory
725 								.createConfig( me.getOperation(), "Monitor Request " + ( row + 1 ) ), -1 );
726 
727 					WsdlTestRequest request = test.getTestRequest();
728 					request.setRequestContent( me.getRequestContent() );
729 					request.setEndpoint( me.getTargetUrl().toString() );
730 
731 					Attachment[] requestAttachments = me.getRequestAttachments();
732 					if( requestAttachments != null )
733 					{
734 						for( Attachment attachment : requestAttachments )
735 						{
736 							request.importAttachment( attachment );
737 						}
738 					}
739 				}
740 				
741 				if( dialog.getBooleanValue( OPENEDITOR ))
742 					UISupport.selectAndShow( testCase );
743 			}
744 		}
745 	}
746 
747 	private final class CreateRequestsAction implements ActionListener
748 	{
749 		public void actionPerformed( ActionEvent e )
750 		{
751 			int[] rows = logTable.getSelectedRows();
752 			if( rows.length == 0 )
753 				return;
754 
755 			if( UISupport.confirm( "Create " + rows.length + " requests", "Create Request" ) )
756 			{
757 				for( int row : rows )
758 				{
759 					WsdlMonitorMessageExchange me = tableModel.getMessageExchangeAt( row );
760 					WsdlRequest request = me.getOperation().addNewRequest( "Monitor Request " + ( row + 1 ) );
761 
762 					request.setRequestContent( me.getRequestContent() );
763 					request.setEndpoint( me.getTargetUrl().toString() );
764 
765 					Attachment[] requestAttachments = me.getRequestAttachments();
766 					if( requestAttachments != null )
767 					{
768 						for( Attachment attachment : requestAttachments )
769 						{
770 							request.importAttachment( attachment );
771 						}
772 					}
773 				}
774 			}
775 		}
776 	}
777 
778 	private final class ClearAction implements ActionListener
779 	{
780 		public void actionPerformed( ActionEvent e )
781 		{
782 			if( logTable.getRowCount() == 0 )
783 				return;
784 
785 			int[] rows = logTable.getSelectedRows();
786 
787 			if( rows.length == 0 )
788 			{
789 				if( UISupport.confirm( "Clear monitor log?", "Clear Log" ) )
790 					tableModel.clear();
791 			}
792 			else if( UISupport.confirm( "Clear " + rows.length + " rows from monitor log?", "Clear Log" ) )
793 			{
794 				tableModel.clearRows( rows );
795 			}
796 		}
797 	}
798 
799 	public class MonitorLogTableModel extends AbstractTableModel
800 	{
801 		private List<WsdlMonitorMessageExchange> exchanges = new LinkedList<WsdlMonitorMessageExchange>();
802 		private DateFormat sdf;
803 
804 		public MonitorLogTableModel()
805 		{
806 			sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
807 		}
808 
809 		public synchronized void clear()
810 		{
811 			int sz = exchanges.size();
812 			while( exchanges.size() > 0 )
813 			{
814 				WsdlMonitorMessageExchange removed = exchanges.remove( 0 );
815 				removed.discard();
816 			}
817 
818 			fireTableRowsDeleted( 0, sz );
819 
820 			while( requestFilterModel.getSize() > 1 )
821 				requestFilterModel.removeElementAt( 1 );
822 
823 			while( targetHostFilterModel.getSize() > 1 )
824 				targetHostFilterModel.removeElementAt( 1 );
825 	
826 			updateRowCountLabel();
827 		}
828 
829 		public synchronized void clearRows( int[] indices )
830 		{
831 			for( int c = indices.length; c > 0; c-- )
832 			{
833 				int index = indices[c - 1];
834 				WsdlMonitorMessageExchange removed = exchanges.remove( logTable.convertRowIndexToModel( index ) );
835 				removed.discard();
836 				fireTableRowsDeleted( index, index );
837 				updateRowCountLabel();
838 			}
839 		}
840 
841 		public int getColumnCount()
842 		{
843 			return 8;
844 		}
845 
846 		public WsdlMonitorMessageExchange getMessageExchangeAt( int tableRow )
847 		{
848 			return exchanges.get( logTable.convertRowIndexToModel( tableRow ) );
849 		}
850 
851 		@Override
852 		public String getColumnName( int column )
853 		{
854 			switch( column )
855 			{
856 			case 0:
857 				return "Time";
858 			case 1:
859 				return "Request Host";
860 			case 2:
861 				return "Target Host";
862 			case 3:
863 				return "Interface";
864 			case 4:
865 				return "Operation";
866 			case 5:
867 				return "Time Taken";
868 			case 6:
869 				return "Req Sz";
870 			case 7:
871 				return "Resp Sz";
872 			}
873 
874 			return null;
875 		}
876 
877 		public int getRowCount()
878 		{
879 			return exchanges.size();
880 		}
881 
882 		public Object getValueAt( int rowIndex, int columnIndex )
883 		{
884 			if( rowIndex < 0 || rowIndex >= exchanges.size() )
885 				return null;
886 			
887 			WsdlMonitorMessageExchange exchange = exchanges.get( rowIndex );
888 
889 			switch( columnIndex )
890 			{
891 			case 0:
892 				return sdf.format( new Date( exchange.getTimestamp() ) );
893 			case 1:
894 				return exchange.getRequestHost();
895 			case 2:
896 				return exchange.getTargetUrl().getHost();
897 			case 3:
898 				return exchange.getOperation() == null ? "- unknown -" : exchange.getOperation().getInterface().getName();
899 			case 4:
900 				return exchange.getOperation() == null ? "- unknown -" : exchange.getOperation().getName();
901 			case 5:
902 				return String.valueOf( exchange.getTimeTaken() );
903 			case 6:
904 				return String.valueOf( exchange.getRequestContentLength() );
905 			case 7:
906 				return String.valueOf( exchange.getResponseContentLength() );
907 			}
908 
909 			return null;
910 		}
911 
912 		public synchronized void addMessageExchange( WsdlMonitorMessageExchange exchange )
913 		{
914 			exchanges.add( exchange );
915 			int size = exchanges.size();
916 			fireTableRowsInserted( size - 1, size );
917 
918 			fitSizeToMaxRows();
919 
920 			String requestHost = exchange.getRequestHost();
921 			if( requestFilterModel.getIndexOf( requestHost ) == -1 )
922 			{
923 				requestFilterModel.addElement( requestHost );
924 			}
925 
926 			String host = exchange.getTargetUrl().getHost();
927 			if( targetHostFilterModel.getIndexOf( host ) == -1 )
928 			{
929 				targetHostFilterModel.addElement( host );
930 			}
931 			
932 			updateRowCountLabel();
933 		}
934 
935 		public void fitSizeToMaxRows()
936 		{
937 			int removeCnt = 0;
938 
939 			while( exchanges.size() > maxRows )
940 			{
941 				WsdlMonitorMessageExchange removed = exchanges.remove( 0 );
942 				removed.discard();
943 				removeCnt++;
944 			}
945 
946 			if( removeCnt > 0 )
947 			{
948 				fireTableDataChanged();
949 				updateRowCountLabel();
950 			}
951 		}
952 	}
953 
954 	protected String getHttpProxyHost()
955 	{
956 		return httpProxyHost;
957 	}
958 
959 	protected void setHttpProxyHost( String proxyHost )
960 	{
961 		httpProxyHost = proxyHost;
962 	}
963 
964 	protected int getHttpProxyPort()
965 	{
966 		return httpProxyPort;
967 	}
968 
969 	protected void setHttpProxyPort( int proxyPort )
970 	{
971 		httpProxyPort = proxyPort;
972 	}
973 
974 	protected SlowLinkSimulator getSlowLink()
975 	{
976 		return slowLink;
977 	}
978 
979 	public String getTargetHost()
980 	{
981 		String host = targetEndpoint;
982 
983 		try
984 		{
985 			URL url = new URL( host );
986 			return url.getHost();
987 		}
988 		catch( MalformedURLException e )
989 		{
990 			return host;
991 		}
992 	}
993 
994 	public String getTargetEndpoint()
995 	{
996 		return targetEndpoint;
997 	}
998 
999 	public int getTargetPort()
1000 	{
1001 		try
1002 		{
1003 			URL url = new URL( targetEndpoint );
1004 			return url.getPort() == -1 ? 80 : url.getPort();
1005 		}
1006 		catch( MalformedURLException e )
1007 		{
1008 			return 80;
1009 		}
1010 	}
1011 
1012 	public int getLocalPort()
1013 	{
1014 		return listenPort;
1015 	}
1016 
1017 	public boolean isProxy()
1018 	{
1019 		return isProxy;
1020 	}
1021 
1022 	public synchronized void addMessageExchange( WsdlMonitorMessageExchange messageExchange )
1023 	{
1024 		messageExchangeStack.push( messageExchange );
1025 
1026 		if( !stackProcessor.isRunning() )
1027 			new Thread( stackProcessor, "SoapMonitor StackProcessor for project [" + getProject().getName() + "]" )
1028 						.start();
1029 	}
1030 
1031 	private class StackProcessor implements Runnable
1032 	{
1033 		private boolean canceled;
1034 		private boolean running;
1035 
1036 		public void run()
1037 		{
1038 			running = true;
1039 			SoapUI.log.info( "Started stackprocessor for soapmonitor in project [" + getProject().getName() + "]" );
1040 			while( !canceled && messageExchangeStack.size() > 0 )
1041 			{
1042 				WsdlMonitorMessageExchange messageExchange = messageExchangeStack.pop();
1043 				if( messageExchange != null )
1044 				{
1045 					processMessage( messageExchange );
1046 				}
1047 
1048 				try
1049 				{
1050 					Thread.sleep( 100 );
1051 				}
1052 				catch( InterruptedException e )
1053 				{
1054 					e.printStackTrace();
1055 				}
1056 			}
1057 			running = false;
1058 		}
1059 
1060 		private synchronized void processMessage( WsdlMonitorMessageExchange messageExchange )
1061 		{
1062 			messageExchange.prepare( 
1063 						project.getWssContainer().getIncomingByName( incomingRequestWss ),
1064 						project.getWssContainer().getIncomingByName( incomingResponseWss ));
1065 			
1066 			tableModel.addMessageExchange( messageExchange );
1067 			if( logTable.getSelectedRow() == logTable.getRowCount() - 2 )
1068 			{
1069 				logTable.setRowSelectionInterval( logTable.getRowCount() - 1, logTable.getRowCount() - 1 );
1070 			}
1071 
1072 			fireOnMessageExchange( messageExchange );
1073 		}
1074 
1075 		public void cancel()
1076 		{
1077 			canceled = true;
1078 		}
1079 
1080 		protected boolean isCanceled()
1081 		{
1082 			return canceled;
1083 		}
1084 
1085 		protected boolean isRunning()
1086 		{
1087 			return running;
1088 		}
1089 	}
1090 
1091 	public MonitorLogTableModel getLogModel()
1092 	{
1093 		return tableModel;
1094 	}
1095 
1096 	public void addSoapMonitorListener( SoapMonitorListener listener )
1097 	{
1098 		listeners.add( listener );
1099 	}
1100 
1101 	public void fireOnMessageExchange( WsdlMonitorMessageExchange messageExchange )
1102 	{
1103 		for( SoapMonitorListener listener : listeners.toArray( new SoapMonitorListener[listeners.size()] ) )
1104 		{
1105 			listener.onMessageExchange( messageExchange );
1106 		}
1107 	}
1108 
1109 	public void removeSoapMonitorListener( SoapMonitorListener listener )
1110 	{
1111 		listeners.remove( listener );
1112 	}
1113 
1114 	public WsdlProject getProject()
1115 	{
1116 		return project;
1117 	}
1118 
1119 	public class SoapMonitorOptionsAction extends AbstractAction
1120 	{
1121 		private static final String CREATE_TCP_TUNNEL = "Create TCP Tunnel";
1122 		private static final String CREATE_HTTP_PROXY = "Create HTTP Proxy";
1123 
1124 		public SoapMonitorOptionsAction()
1125 		{
1126 			putValue( SMALL_ICON, UISupport.createImageIcon( "/options.gif" ) );
1127 		}
1128 
1129 		public void actionPerformed( ActionEvent e )
1130 		{
1131 			if( optionsDialog == null )
1132 			{
1133 				optionsDialog = ADialogBuilder.buildDialog( OptionsForm.class );
1134 				optionsDialog.getFormField( OptionsForm.MODE ).addFormFieldListener( new XFormFieldListener()
1135 				{
1136 
1137 					public void valueChanged( XFormField sourceField, String newValue, String oldValue )
1138 					{
1139 						optionsDialog.getFormField( OptionsForm.TARGET_HOST ).setEnabled( !newValue.equals( CREATE_HTTP_PROXY ) );
1140 						optionsDialog.getFormField( OptionsForm.ADD_ENDPOINT ).setEnabled( !newValue.equals( CREATE_HTTP_PROXY ) );
1141 					}
1142 				} );
1143 			}
1144 
1145 			StringList endpoints = new StringList();
1146 			endpoints.add( null );
1147 
1148 			for( Interface iface : getProject().getInterfaceList() )
1149 			{
1150 				endpoints.addAll( iface.getEndpoints() );
1151 			}
1152 
1153 			optionsDialog.setOptions( OptionsForm.TARGET_HOST, endpoints.toStringArray() );
1154 
1155 			optionsDialog.setIntValue( OptionsForm.PORT, listenPort );
1156 			optionsDialog.setValue( OptionsForm.TARGET_HOST, targetEndpoint );
1157 			optionsDialog.setValue( OptionsForm.MODE, isProxy ? "Create HTTP Proxy" : "Create TCP Tunnel" );
1158 			optionsDialog.setIntValue( OptionsForm.MAXROWS, maxRows );
1159 			optionsDialog.setBooleanValue( OptionsForm.ADD_ENDPOINT, addEndpoint );
1160 			
1161 			optionsDialog.setOptions( OptionsForm.REQUEST_WSS, 
1162 						StringUtils.merge( project.getWssContainer().getIncomingNames(), "<none>" ) );
1163 			optionsDialog.setOptions( OptionsForm.RESPONSE_WSS, 
1164 						StringUtils.merge( project.getWssContainer().getIncomingNames(), "<none>" ) );
1165 			
1166 			optionsDialog.setValue( OptionsForm.REQUEST_WSS, incomingRequestWss );
1167 			optionsDialog.setValue( OptionsForm.RESPONSE_WSS, incomingResponseWss );
1168 
1169 			optionsDialog.getFormField( OptionsForm.TARGET_HOST ).setEnabled( !isProxy );
1170 			optionsDialog.getFormField( OptionsForm.ADD_ENDPOINT ).setEnabled( !isProxy );
1171 			
1172 			if( optionsDialog.show() )
1173 			{
1174 				Settings settings = getProject().getSettings();
1175 
1176 				settings.setLong( OptionsForm.PORT, listenPort = optionsDialog.getIntValue( OptionsForm.PORT, listenPort ) );
1177 				settings.setLong( OptionsForm.MAXROWS, maxRows = optionsDialog.getIntValue( OptionsForm.MAXROWS, maxRows ) );
1178 				settings.setString( OptionsForm.TARGET_HOST, targetEndpoint = optionsDialog.getValue( OptionsForm.TARGET_HOST ) );
1179 				String mode = optionsDialog.getValue( OptionsForm.MODE );
1180 				isProxy = mode.equals( "Create HTTP Proxy" );
1181 				settings.setString( OptionsForm.MODE, mode );
1182 				
1183 				incomingRequestWss = optionsDialog.getValue( OptionsForm.REQUEST_WSS );
1184 				incomingResponseWss = optionsDialog.getValue( OptionsForm.RESPONSE_WSS );
1185 
1186 				tableModel.fitSizeToMaxRows();
1187 			}
1188 		}
1189 
1190 		@AForm( name = "MockService Options", description = "Set options for this MockService", helpUrl = HelpUrls.MOCKSERVICEOPTIONS_HELP_URL, icon = UISupport.OPTIONS_ICON_PATH )
1191 		private class OptionsForm
1192 		{
1193 			@AField( description = "The local port to listen on", name = "Port", type = AFieldType.INT )
1194 			public final static String PORT = "Port";
1195 
1196 			@AField( description = "Specifies monitor mode", name = "Mode", type = AFieldType.RADIOGROUP, values = {
1197 						CREATE_TCP_TUNNEL, CREATE_HTTP_PROXY } )
1198 			public final static String MODE = "Mode";
1199 
1200 			@AField( description = "The target host to invoke", name = "Target Host", type = AFieldType.ENUMERATION )
1201 			public final static String TARGET_HOST = "Target Host";
1202 
1203 			@AField( description = "Adds an endpoint for the Tcp Tunnel", name = "Add Endpoint", type = AFieldType.BOOLEAN )
1204 			public final static String ADD_ENDPOINT = "Add Endpoint";
1205 
1206 			@AField( description = "The maximum number of exchanges to log", name = "Max Log", type = AFieldType.INT )
1207 			public final static String MAXROWS = "Max Log";
1208 			
1209 			@AField(description = "The Incoming WSS configuration to use for processing requests", name = "Incoming Request WSS", type=AFieldType.ENUMERATION )
1210 			public final static String REQUEST_WSS = "Incoming Request WSS";
1211 
1212 			@AField(description = "The Outgoing WSS configuration to use for processing responses", name = "Incoming Response WSS", type=AFieldType.ENUMERATION )
1213 			public final static String RESPONSE_WSS = "Incoming Response WSS";
1214 		}
1215 	}
1216 
1217 	public void release()
1218 	{
1219 		requestViewer.release();
1220 		responseViewer.release();
1221 		
1222 		if( optionsDialog != null )
1223 		{
1224 			optionsDialog.release();
1225 			optionsDialog = null;
1226 		}
1227 	}
1228 }