View Javadoc

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