/*
 *  soapUI, copyright (C) 2004-2012 smartbear.com 
 *
 *  soapUI is free software; you can redistribute it and/or modify it under the 
 *  terms of version 2.1 of the GNU Lesser General Public License as published by 
 *  the Free Software Foundation.
 *
 *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
 *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 *  See the GNU Lesser General Public License for more details at gnu.org.
 */

package com.eviware.soapui.impl.wsdl.monitor;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;

import org.apache.commons.collections.list.TreeList;
import org.apache.http.HttpRequest;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.Filter;
import org.jdesktop.swingx.decorator.FilterPipeline;
import org.jdesktop.swingx.decorator.PatternFilter;

import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.support.AbstractInterface;
import com.eviware.soapui.impl.wsdl.WsdlInterface;
import com.eviware.soapui.impl.wsdl.WsdlProject;
import com.eviware.soapui.impl.wsdl.WsdlRequest;
import com.eviware.soapui.impl.wsdl.WsdlTestSuite;
import com.eviware.soapui.impl.wsdl.actions.monitor.SoapMonitorAction;
import com.eviware.soapui.impl.wsdl.actions.monitor.SoapMonitorAction.LaunchForm;
import com.eviware.soapui.impl.wsdl.mock.WsdlMockOperation;
import com.eviware.soapui.impl.wsdl.mock.WsdlMockResponse;
import com.eviware.soapui.impl.wsdl.mock.WsdlMockService;
import com.eviware.soapui.impl.wsdl.support.HelpUrls;
import com.eviware.soapui.impl.wsdl.support.MessageExchangeModelItem;
import com.eviware.soapui.impl.wsdl.support.MessageExchangeRequestMessageEditor;
import com.eviware.soapui.impl.wsdl.support.MessageExchangeResponseMessageEditor;
import com.eviware.soapui.impl.wsdl.testcase.WsdlTestCase;
import com.eviware.soapui.impl.wsdl.teststeps.HttpTestRequest;
import com.eviware.soapui.impl.wsdl.teststeps.HttpTestRequestStep;
import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequest;
import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep;
import com.eviware.soapui.impl.wsdl.teststeps.registry.HttpRequestStepFactory;
import com.eviware.soapui.impl.wsdl.teststeps.registry.WsdlTestRequestStepFactory;
import com.eviware.soapui.model.iface.Attachment;
import com.eviware.soapui.model.iface.Interface;
import com.eviware.soapui.model.settings.Settings;
import com.eviware.soapui.model.support.ModelSupport;
import com.eviware.soapui.model.testsuite.TestSuite;
import com.eviware.soapui.settings.ProxySettings;
import com.eviware.soapui.support.DateUtil;
import com.eviware.soapui.support.StringUtils;
import com.eviware.soapui.support.UISupport;
import com.eviware.soapui.support.components.BrowserComponent;
import com.eviware.soapui.support.components.JComponentInspector;
import com.eviware.soapui.support.components.JInspectorPanel;
import com.eviware.soapui.support.components.JInspectorPanelFactory;
import com.eviware.soapui.support.components.JXToolBar;
import com.eviware.soapui.support.types.StringList;
import com.eviware.soapui.support.types.StringToStringsMap;
import com.eviware.x.form.XFormDialog;
import com.eviware.x.form.XFormField;
import com.eviware.x.form.XFormFieldListener;
import com.eviware.x.form.support.ADialogBuilder;
import com.eviware.x.form.support.AField;
import com.eviware.x.form.support.AField.AFieldType;
import com.eviware.x.form.support.AForm;
import com.jgoodies.forms.builder.ButtonBarBuilder;

/**
 * A SOAP Monitor..
 */

@SuppressWarnings( "serial" )
public class SoapMonitor extends JPanel
{
	private static final String ALL_FILTER_OPTION = "- all -";
	private JProgressBar progressBar;
	private JButton stopButton = null;

	private JXTable logTable = null;
	private MonitorLogTableModel tableModel = null;

	private String httpProxyHost = null;
	private int httpProxyPort = 80;

	private JButton startButton;
	private final WsdlProject project;
	private MessageExchangeRequestMessageEditor requestViewer;
	private MessageExchangeResponseMessageEditor responseViewer;
	private SoapUIListenerSupport<MonitorListener> listeners = new SoapUIListenerSupport<MonitorListener>(
			MonitorListener.class );
	private MessageExchangeModelItem requestModelItem;
	private JButton optionsButton;
	private int listenPort;
	private String targetEndpoint;
	private JButton clearButton;
	private int maxRows;
	private JButton addToTestCaseButton;
	// private JButton addToRestTestCaseButton;
	private JButton createRequestButton;
	private JButton addToMockServiceButton;
	private Stack<WsdlMonitorMessageExchange> messageExchangeStack = new Stack<WsdlMonitorMessageExchange>();
	private StackProcessor stackProcessor = new StackProcessor();
	private PatternFilter operationFilter;
	private PatternFilter interfaceFilter;
	private PatternFilter targetHostFilter;
	private JLabel infoLabel;
	private PatternFilter requestHostFilter;
	private JComboBox requestHostFilterCombo;
	private JComboBox targetHostFilterCombo;
	private JComboBox interfaceFilterCombo;
	private JComboBox operationFilterCombo;
	private DefaultComboBoxModel operationFilterModel;
	private DefaultComboBoxModel requestFilterModel;
	private DefaultComboBoxModel targetHostFilterModel;
	private JLabel rowCountLabel = new JLabel();
	private Map<AbstractInterface<?>, String> addedEndpoints;
	private JXToolBar toolbar;
	private String incomingRequestWss;
	private String incomingResponseWss;
	private boolean setAsProxy;
	private XFormDialog optionsDialog;
	private SoapMonitorEngine monitorEngine;
	private String oldProxyHost;
	private String oldProxyPort;
	private boolean oldProxyEnabled;
	private String sslEndpoint;
	private JInspectorPanel inspectorPanel;

	public SoapMonitor( WsdlProject project, int listenPort, String incomingRequestWss, String incomingResponseWss,
			JXToolBar mainToolbar, boolean setAsProxy, String sslEndpoint )
	{
		super( new BorderLayout() );
		this.project = project;
		this.listenPort = listenPort;
		this.incomingRequestWss = incomingRequestWss;
		this.incomingResponseWss = incomingResponseWss;
		this.setAsProxy = setAsProxy;
		this.maxRows = 100;
		this.sslEndpoint = sslEndpoint;

		// set the slow link to the passed down link

		this.setLayout( new BorderLayout() );

		add( buildToolbars( mainToolbar ), BorderLayout.NORTH );
		add( buildContent(), BorderLayout.CENTER );

		start();
	}

	public SoapMonitor( WsdlProject project, int sourcePort, String incomingRequestWss, String incomingResponseWss,
			JXToolBar toolbar, boolean setAsProxy )
	{
		this( project, sourcePort, incomingRequestWss, incomingResponseWss, toolbar, setAsProxy, null );
	}

	private JComponent buildContent()
	{
		inspectorPanel = JInspectorPanelFactory.build( buildLog() );

		JComponentInspector<JComponent> viewInspector = new JComponentInspector<JComponent>( buildViewer(),
				"Message Content", "Shows message content", true );
		inspectorPanel.addInspector( viewInspector );

		return inspectorPanel.getComponent();
	}

	private JComponent buildLog()
	{
		tableModel = new MonitorLogTableModel();
		logTable = new JXTable( 1, 2 );
		logTable.setColumnControlVisible( true );
		logTable.setModel( tableModel );
		logTable.setHorizontalScrollEnabled( true );
		logTable.setSelectionMode( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION );

		operationFilter = new PatternFilter( ".*", 0, 4 );
		operationFilter.setAcceptNull( true );
		interfaceFilter = new PatternFilter( ".*", 0, 3 );
		interfaceFilter.setAcceptNull( true );
		targetHostFilter = new PatternFilter( ".*", 0, 2 );
		targetHostFilter.setAcceptNull( true );
		requestHostFilter = new PatternFilter( ".*", 0, 1 );
		requestHostFilter.setAcceptNull( true );

		Filter[] filters = new Filter[] { requestHostFilter, targetHostFilter, interfaceFilter, operationFilter };

		FilterPipeline pipeline = new FilterPipeline( filters );
		logTable.setFilters( pipeline );

		ListSelectionModel sel = logTable.getSelectionModel();
		sel.addListSelectionListener( new ListSelectionListener()
		{
			public void valueChanged( ListSelectionEvent event )
			{
				int row = logTable.getSelectedRow();
				if( row == -1 )
				{
					// requestXmlDocument.setXml( null );
					// responseXmlDocument.setXml( null );
					requestModelItem.setMessageExchange( null );
				}
				else
				{
					WsdlMonitorMessageExchange exchange = tableModel.getMessageExchangeAt( row );
					requestModelItem.setMessageExchange( exchange );
					// responseModelItem.setMessageExchange( exchange );
					// requestXmlDocument.setXml( exchange.getRequestContent() );
					// responseXmlDocument.setXml( exchange.getResponseContent() );
				}

				addToMockServiceButton.setEnabled( row != -1 );
				addToTestCaseButton.setEnabled( row != -1 );
				// addToRestTestCaseButton.setEnabled( row != -1 );
				createRequestButton.setEnabled( row != -1 );
			}
		} );

		JPanel tablePane = new JPanel();
		tablePane.setLayout( new BorderLayout() );

		toolbar.addGlue();

		tablePane.add( buildFilterBar(), BorderLayout.NORTH );
		tablePane.add( new JScrollPane( logTable ), BorderLayout.CENTER );

		return tablePane;
	}

	private JPanel buildFilterBar()
	{
		requestFilterModel = new DefaultComboBoxModel( new String[] { ALL_FILTER_OPTION } );
		targetHostFilterModel = new DefaultComboBoxModel( new String[] { ALL_FILTER_OPTION } );
		Dimension comboBoxSize = new Dimension( 90, 18 );
		requestHostFilterCombo = UISupport.setFixedSize( new JComboBox( requestFilterModel ), comboBoxSize );

		// toolbar.addFixed( new JLabel( "<html><b>Filter:</b></html>"));
		// toolbar.addUnrelatedGap();

		ButtonBarBuilder toolbar = new ButtonBarBuilder();

		toolbar.addFixed( new JLabel( "Request Host" ) );
		toolbar.addRelatedGap();
		toolbar.addFixed( requestHostFilterCombo );
		toolbar.addUnrelatedGap();

		requestHostFilterCombo.addItemListener( new ItemListener()
		{
			public void itemStateChanged( ItemEvent e )
			{
				int ix = requestHostFilterCombo.getSelectedIndex();
				if( ix == -1 )
					return;

				requestHostFilter.setAcceptNull( ix == 0 );

				if( ix == 0 )
					requestHostFilter.setPattern( ".*", 0 );
				else
					requestHostFilter.setPattern( requestHostFilterCombo.getSelectedItem().toString(), 0 );

				updateRowCountLabel();
			}
		} );

		toolbar.addFixed( new JLabel( "Target Host" ) );
		toolbar.addRelatedGap();
		targetHostFilterCombo = UISupport.setFixedSize( new JComboBox( targetHostFilterModel ), comboBoxSize );
		toolbar.addFixed( targetHostFilterCombo );
		toolbar.addUnrelatedGap();

		targetHostFilterCombo.addItemListener( new ItemListener()
		{
			public void itemStateChanged( ItemEvent e )
			{
				int ix = targetHostFilterCombo.getSelectedIndex();
				if( ix == -1 )
					return;

				targetHostFilter.setAcceptNull( ix == 0 );

				if( ix == 0 )
					targetHostFilter.setPattern( ".*", 0 );
				else
					targetHostFilter.setPattern( targetHostFilterCombo.getSelectedItem().toString(), 0 );

				updateRowCountLabel();
			}
		} );

		String[] interfaceNames = ModelSupport.getNames( new String[] { ALL_FILTER_OPTION },
				ModelSupport.getChildren( getProject(), WsdlInterface.class ) );

		toolbar.addFixed( new JLabel( "Interface" ) );
		toolbar.addRelatedGap();
		interfaceFilterCombo = UISupport.setFixedSize( new JComboBox( interfaceNames ), comboBoxSize );
		toolbar.addFixed( interfaceFilterCombo );
		toolbar.addUnrelatedGap();

		operationFilterModel = new DefaultComboBoxModel( new String[] { ALL_FILTER_OPTION } );
		interfaceFilterCombo.addItemListener( new ItemListener()
		{
			public void itemStateChanged( ItemEvent e )
			{
				String item = ( String )interfaceFilterCombo.getSelectedItem();
				operationFilterModel.removeAllElements();

				if( item == null || getProject().getInterfaceByName( item ) == null )
				{
					operationFilterModel.addElement( ALL_FILTER_OPTION );
					interfaceFilter.setPattern( ".*", 0 );
				}
				else if( getProject().getInterfaceByName( item ) != null )
				{
					WsdlInterface iface = ( WsdlInterface )getProject().getInterfaceByName( item );
					String[] operationNames = ModelSupport.getNames( new String[] { ALL_FILTER_OPTION },
							iface.getOperationList() );
					for( String s : operationNames )
						operationFilterModel.addElement( s );

					interfaceFilter.setPattern( iface.getName(), 0 );
				}
			}
		} );

		toolbar.addFixed( new JLabel( "Operation" ) );
		toolbar.addRelatedGap();
		operationFilterCombo = UISupport.setFixedSize( new JComboBox( operationFilterModel ), comboBoxSize );
		toolbar.addFixed( operationFilterCombo );

		operationFilterCombo.addItemListener( new ItemListener()
		{
			public void itemStateChanged( ItemEvent e )
			{
				int ix = operationFilterCombo.getSelectedIndex();
				if( ix == -1 )
				{
					operationFilter.setPattern( ".*", 0 );
					updateRowCountLabel();
					return;
				}

				operationFilter.setAcceptNull( ix == 0 );

				if( ix == 0 )
					operationFilter.setPattern( ".*", 0 );
				else
					operationFilter.setPattern( operationFilterCombo.getSelectedItem().toString(), 0 );

				updateRowCountLabel();
			}
		} );

		toolbar.setBorder( BorderFactory.createEmptyBorder( 3, 2, 3, 0 ) );
		return toolbar.getPanel();
	}

	protected void updateRowCountLabel()
	{
		rowCountLabel.setText( logTable.getRowCount() + "/" + tableModel.getRowCount() + " entries" );
	}

	private JComponent buildViewer()
	{
		requestModelItem = new MessageExchangeModelItem( "monitor message exchange", null )
		{

			@Override
			public boolean hasRawData()
			{
				return true;
			}
		};

		requestViewer = new MessageExchangeRequestMessageEditor( requestModelItem );
		responseViewer = new MessageExchangeResponseMessageEditor( requestModelItem );

		return UISupport.createHorizontalSplit( requestViewer, responseViewer );
	}

	private JComponent buildToolbars( JXToolBar mainToolbar )
	{
		toolbar = UISupport.createSmallToolbar();
		mainToolbar.addFixed( startButton = UISupport.createToolbarButton( UISupport
				.createImageIcon( "/run_testcase.gif" ) ) );
		mainToolbar.addFixed( stopButton = UISupport.createToolbarButton( UISupport
				.createImageIcon( "/stop_testcase.gif" ) ) );
		mainToolbar.addFixed( optionsButton = UISupport.createToolbarButton( new SoapMonitorOptionsAction() ) );

		toolbar
				.addFixed( createRequestButton = UISupport.createToolbarButton( UISupport.createImageIcon( "/request.gif" ) ) );
		toolbar.addFixed( addToTestCaseButton = UISupport.createToolbarButton( UISupport
				.createImageIcon( "/testCase.gif" ) ) );
		// toolbar.addFixed( addToRestTestCaseButton =
		// UISupport.createToolbarButton( UISupport
		// .createImageIcon( "/testCase.gif" ) ) );
		toolbar.addFixed( addToMockServiceButton = UISupport.createToolbarButton( UISupport
				.createImageIcon( "/mockService.gif" ) ) );
		toolbar
				.addFixed( clearButton = UISupport.createToolbarButton( UISupport.createImageIcon( "/clear_loadtest.gif" ) ) );

		startButton.setToolTipText( "Starts the HTTP Monitor as configured" );
		stopButton.setToolTipText( "Stops the HTTP Monitor" );
		optionsButton.setToolTipText( "Sets Monitor Options" );
		clearButton.setToolTipText( "Clear all/selected messages from the log" );
		createRequestButton.setToolTipText( "Creates requests from selected messages" );
		addToTestCaseButton.setToolTipText( "Adds selected requests to a TestCase" );
		// addToRestTestCaseButton.setToolTipText(
		// "Adds selected REST requests to a TestCase" );
		addToMockServiceButton.setToolTipText( "Adds selected reponses to a MockService" );

		createRequestButton.setEnabled( false );
		addToMockServiceButton.setEnabled( false );
		addToTestCaseButton.setEnabled( false );
		// addToRestTestCaseButton.setEnabled( false );

		startButton.addActionListener( new ActionListener()
		{
			public void actionPerformed( ActionEvent e )
			{
				start();
			}
		} );

		stopButton.addActionListener( new ActionListener()
		{
			public void actionPerformed( ActionEvent e )
			{
				stop();
			}
		} );

		clearButton.addActionListener( new ClearAction() );
		createRequestButton.addActionListener( new CreateRequestsAction() );
		addToTestCaseButton.addActionListener( new AddToTestCaseAction() );
		// addToRestTestCaseButton.addActionListener( new
		// AddToRESTTestCaseAction() );
		addToMockServiceButton.addActionListener( new AddToMockServiceAction() );

		mainToolbar.addGlue();

		infoLabel = new JLabel();
		infoLabel.setPreferredSize( new Dimension( 150, 20 ) );
		infoLabel.setOpaque( false );
		mainToolbar.addFixed( infoLabel );

		progressBar = new JProgressBar();

		JPanel progressBarPanel = UISupport.createProgressBarPanel( progressBar, 2, false );
		progressBarPanel.setPreferredSize( new Dimension( 60, 20 ) );

		mainToolbar.addFixed( progressBarPanel );
		return toolbar;
	}

	/**
	 * Method start
	 */
	public void start()
	{
		int localPort = getLocalPort();
		// monitorEngine = new TcpMonMonitorEngine();

		monitorEngine = new SoapMonitorEngineImpl();
		( ( SoapMonitorEngineImpl )monitorEngine ).setSslEndpoint( sslEndpoint );
		monitorEngine.start( this, localPort );

		if( monitorEngine.isRunning() )
		{
			stopButton.setEnabled( true );
			startButton.setEnabled( false );
			optionsButton.setEnabled( false );
			infoLabel.setText( ( monitorEngine.isProxy() ? "HTTP Proxy " : "SSL Tunnel " ) + "on port " + localPort );
			progressBar.setIndeterminate( true );

			if( setAsProxy )
			{
				oldProxyHost = SoapUI.getSettings().getString( ProxySettings.HOST, "" );
				oldProxyPort = SoapUI.getSettings().getString( ProxySettings.PORT, "" );
				oldProxyEnabled = SoapUI.getSettings().getBoolean( ProxySettings.ENABLE_PROXY );

				SoapUI.getSettings().setString( ProxySettings.HOST, "127.0.0.1" );
				SoapUI.getSettings().setString( ProxySettings.PORT, String.valueOf( localPort ) );
				SoapUI.getSettings().setBoolean( ProxySettings.ENABLE_PROXY, true );
				SoapUI.setProxyEnabled( true );
				JToggleButton applyProxyButton = ( JToggleButton )SoapUI.getApplyProxyButton();
				if( applyProxyButton != null )
					applyProxyButton.setIcon( UISupport.createImageIcon( SoapUI.PROXY_ENABLED_ICON ) );
			}

			SoapUI.log.info( "Started HTTP Monitor on local port " + localPort );
		}
		else
		{
			stopButton.setEnabled( false );
			startButton.setEnabled( true );
			optionsButton.setEnabled( true );
			infoLabel.setText( "Stopped" );
			progressBar.setIndeterminate( false );

			SoapUI.log.info( "Could not start HTTP Monitor on local port " + localPort );
		}
	}

	/**
	 * Method close
	 */
	public void close()
	{
		stop();
	}

	/**
	 * Method stop
	 */
	public void stop()
	{
		monitorEngine.stop();
		if( addedEndpoints != null )
		{
			for( Interface iface : addedEndpoints.keySet() )
				iface.removeEndpoint( addedEndpoints.get( iface ) );

			addedEndpoints.clear();
		}

		stopButton.setEnabled( false );
		startButton.setEnabled( true );
		optionsButton.setEnabled( true );
		progressBar.setIndeterminate( false );
		infoLabel.setText( "Stopped" );

		if( setAsProxy )
		{
			SoapUI.getSettings().setString( ProxySettings.HOST, oldProxyHost );
			SoapUI.getSettings().setString( ProxySettings.PORT, oldProxyPort );
			SoapUI.getSettings().setBoolean( ProxySettings.ENABLE_PROXY, oldProxyEnabled );
			SoapUI.setProxyEnabled( oldProxyEnabled );
			JToggleButton applyProxyButton = ( JToggleButton )SoapUI.getApplyProxyButton();
			if( applyProxyButton != null )
			{
				if( oldProxyEnabled )
				{
					applyProxyButton.setIcon( UISupport.createImageIcon( SoapUI.PROXY_ENABLED_ICON ) );
				}
				else
				{
					applyProxyButton.setIcon( UISupport.createImageIcon( SoapUI.PROXY_DISABLED_ICON ) );
				}
			}
		}
	}

	@AForm( description = "Set options for adding selected requests to a MockService", name = "Add To MockService" )
	private final class AddToMockServiceAction implements ActionListener
	{
		private static final String CREATE_NEW_OPTION = "<Create New>";
		private XFormDialog dialog;

		@AField( name = "Target MockService", description = "The target TestSuite", type = AFieldType.ENUMERATION )
		public final static String MOCKSERVICE = "Target MockService";

		@AField( name = "Open Editor", description = "Open the created MockService", type = AFieldType.BOOLEAN )
		public final static String OPENEDITOR = "Open Editor";

		public void actionPerformed( ActionEvent e )
		{
			int[] rows = logTable.getSelectedRows();
			if( rows.length == 0 )
				return;

			if( dialog == null )
			{
				dialog = ADialogBuilder.buildDialog( this.getClass() );
			}

			String[] testSuiteNames = ModelSupport.getNames( new String[] { CREATE_NEW_OPTION }, getProject()
					.getMockServiceList() );
			dialog.setOptions( MOCKSERVICE, testSuiteNames );

			if( dialog.show() )
			{
				int withoutOperation = 0;
				for( int row : rows )
				{
					WsdlMonitorMessageExchange me = tableModel.getMessageExchangeAt( row );
					if( me.getOperation() == null )
						withoutOperation++ ;
				}
				if( withoutOperation == rows.length )
				{
					UISupport.showInfoMessage( "No SOAP requests selected!" );
					return;
				}
				String targetMockServiceName = dialog.getValue( MOCKSERVICE );

				WsdlMockService mockService = getProject().getMockServiceByName( targetMockServiceName );
				if( mockService == null )
				{
					targetMockServiceName = ModelSupport.promptForUniqueName( "MockService", getProject(), "" );
					if( targetMockServiceName == null )
						return;

					mockService = getProject().addNewMockService( targetMockServiceName );
					mockService.setIncomingWss( incomingResponseWss );
				}

				int cnt = 0;
				for( int row : rows )
				{
					WsdlMonitorMessageExchange me = tableModel.getMessageExchangeAt( row );
					if( me.getOperation() == null )
						continue;

					WsdlMockOperation mockOperation = mockService.getMockOperation( me.getOperation() );
					if( mockOperation == null )
						mockOperation = mockService.addNewMockOperation( me.getOperation() );

					WsdlMockResponse mockResponse = mockOperation
							.addNewMockResponse( "Monitor Response " + ( ++cnt ), false );
					mockResponse.setResponseContent( me.getResponseContent() );

					Attachment[] requestAttachments = me.getResponseAttachments();
					if( requestAttachments != null )
					{
						for( Attachment attachment : requestAttachments )
						{
							mockResponse.addAttachment( attachment );
						}
					}
				}

				if( cnt == 0 )
				{
					UISupport.showInfoMessage( "No response messages found" );
				}
				else
				{
					UISupport.showInfoMessage( "Added " + cnt + " MockResponses to MockService" );

					if( dialog.getBooleanValue( OPENEDITOR ) )
						UISupport.selectAndShow( mockService );
				}
			}
		}
	}

	@AForm( description = "Set options for adding selected requests to a TestCase", name = "Add To TestCase" )
	private final class AddToTestCaseAction implements ActionListener
	{
		private static final String CREATE_NEW_OPTION = "<Create New>";
		private XFormDialog dialog;

		@AField( name = "Target TestSuite", description = "The target TestSuite", type = AFieldType.ENUMERATION )
		public final static String TESTSUITE = "Target TestSuite";

		@AField( name = "Target TestCase", description = "The target TestCase for the requests", type = AFieldType.ENUMERATION )
		public final static String TESTCASE = "Target TestCase";

		@AField( name = "Open Editor", description = "Open the created TestCase", type = AFieldType.BOOLEAN )
		public final static String OPENEDITOR = "Open Editor";

		TestSuite testSuite;

		public void actionPerformed( ActionEvent e )
		{
			int[] rows = logTable.getSelectedRows();
			if( rows.length == 0 )
				return;

			if( dialog == null )
			{
				dialog = ADialogBuilder.buildDialog( this.getClass() );
				dialog.getFormField( TESTSUITE ).addFormFieldListener( new XFormFieldListener()
				{
					public void valueChanged( XFormField sourceField, String newValue, String oldValue )
					{
						if( newValue.equals( CREATE_NEW_OPTION ) )
						{
							dialog.setOptions( TESTCASE, new String[] { CREATE_NEW_OPTION } );
						}
						else
						{
							testSuite = getProject().getTestSuiteByName( newValue );
							dialog.setOptions(
									TESTCASE,
									testSuite == null ? new String[] { CREATE_NEW_OPTION } : ModelSupport.getNames(
											testSuite.getTestCaseList(), new String[] { CREATE_NEW_OPTION } ) );
						}
					}
				} );
			}

			String[] testSuiteNames = ModelSupport.getNames( new String[] { CREATE_NEW_OPTION }, getProject()
					.getTestSuiteList() );
			dialog.setOptions( TESTSUITE, testSuiteNames );
			testSuite = getProject().getTestSuiteByName( dialog.getValue( TESTSUITE ) );
			dialog.setOptions(
					TESTCASE,
					testSuite == null ? new String[] { CREATE_NEW_OPTION } : ModelSupport.getNames(
							testSuite.getTestCaseList(), new String[] { CREATE_NEW_OPTION } ) );

			if( dialog.show() )
			{
				String targetTestSuiteName = dialog.getValue( TESTSUITE );
				String targetTestCaseName = dialog.getValue( TESTCASE );

				WsdlTestSuite testSuite = getProject().getTestSuiteByName( targetTestSuiteName );
				if( testSuite == null )
				{
					targetTestSuiteName = ModelSupport.promptForUniqueName( "TestSuite", getProject(), "" );
					if( targetTestSuiteName == null )
						return;

					testSuite = getProject().addNewTestSuite( targetTestSuiteName );
				}

				WsdlTestCase testCase = testSuite.getTestCaseByName( targetTestCaseName );
				if( testCase == null )
				{
					targetTestCaseName = ModelSupport.promptForUniqueName( "TestCase", testSuite, "" );
					if( targetTestCaseName == null )
						return;

					testCase = testSuite.addNewTestCase( targetTestCaseName );
				}

				for( int row : rows )
				{
					WsdlMonitorMessageExchange me = tableModel.getMessageExchangeAt( row );
					if( me.getOperation() != null )
					{
						WsdlTestRequestStep test = ( WsdlTestRequestStep )testCase.insertTestStep(
								WsdlTestRequestStepFactory.createConfig( me.getOperation(), "Monitor Request " + ( row + 1 ) ),
								-1 );

						WsdlTestRequest request = test.getTestRequest();
						request.setRequestContent( me.getRequestContent() );
						request.setEndpoint( me.getTargetUrl().toString() );
						request.setIncomingWss( incomingRequestWss );

						Attachment[] requestAttachments = me.getRequestAttachments();
						if( requestAttachments != null )
						{
							for( Attachment attachment : requestAttachments )
							{
								request.importAttachment( attachment );
							}
						}
					}
					else
					{

						// if( me.getResponseContentType().contains( "/x-amf" ) )
						// {
						// AMFRequestStepFactory httpRequestStepFactory = new
						// AMFRequestStepFactory();
						// AMFRequestTestStep test = ( AMFRequestTestStep
						// )testCase.insertTestStep( httpRequestStepFactory
						// .createConfig( me, "Monitor Request " + ( row + 1 ) ), -1
						// );
						//
						// AMFRequest request = test.getAMFRequest();
						// // request.setRequestContent( me.getRequestContent() );
						// request.setEndpoint( me.getTargetUrl().toString() );
						// request.setHttpHeaders( me.getRequestHeaders() );
						//
						// }
						// else
						// {
						HttpRequestStepFactory httpRequestStepFactory = new HttpRequestStepFactory();
						HttpTestRequestStep test = ( HttpTestRequestStep )testCase.insertTestStep(
								httpRequestStepFactory.createConfig( me, "Monitor Request " + ( row + 1 ) ), -1 );

						test.getTestRequest().setRequestHeaders( excludeHeaders( me.getRequestHeaders() ) );

						HttpTestRequest request = ( HttpTestRequest )test.getHttpRequest();

						request.setEndpoint( me.getTargetUrl().toString() );
						// request.setIncomingWss( incomingRequestWss );
						String existingMediaType = me.getResponseHeaders().get( "Content-Type", "" );
						if( !StringUtils.isNullOrEmpty( existingMediaType ) )
						{
							request.setMediaType( existingMediaType );
						}
						if( "application/octet-stream".equals( existingMediaType )
								|| "application/x-amf".equals( existingMediaType ) )
						{
							request.attachBinaryData( me.getRequestContent().getBytes(), existingMediaType );
						}
						else
						{
							request.setRequestContent( me.getRequestContent() );
							test.getTestRequest().setRequestContent( me.getRequestContent() );
						}
						Attachment[] requestAttachments = me.getRequestAttachments();
						if( requestAttachments != null )
						{
							for( Attachment attachment : requestAttachments )
							{
								request.importAttachment( attachment );
							}
						}
						// }

					}
				}

				if( dialog.getBooleanValue( OPENEDITOR ) )
					UISupport.selectAndShow( testCase );
			}
		}
	}

	// @AForm( description =
	// "Set options for adding selected REST requests to a TestCase", name =
	// "Add Rest Test Step(s) To TestCase" )
	// // private final class AddToRESTTestCaseAction implements ActionListener
	// {
	// private static final String CREATE_NEW_OPTION = "<Create New>";
	// private XFormDialog dialog;
	//
	// @AField( name = "Target TestSuite", description = "The target TestSuite",
	// type = AFieldType.ENUMERATION )
	// public final static String TESTSUITE = "Target TestSuite";
	//
	// @AField( name = "Target TestCase", description =
	// "The target TestCase for the requests", type = AFieldType.ENUMERATION )
	// public final static String TESTCASE = "Target TestCase";
	//
	// @AField( name = "Open Editor", description = "Open the created TestCase",
	// type = AFieldType.BOOLEAN )
	// public final static String OPENEDITOR = "Open Editor";
	//
	// TestSuite testSuite;
	//
	// public void actionPerformed( ActionEvent e )
	// {
	// int[] rows = logTable.getSelectedRows();
	// if( rows.length == 0 )
	// return;
	//
	// if( dialog == null )
	// {
	// dialog = ADialogBuilder.buildDialog( this.getClass() );
	// dialog.getFormField( TESTSUITE ).addFormFieldListener( new
	// XFormFieldListener()
	// {
	// public void valueChanged( XFormField sourceField, String newValue, String
	// oldValue )
	// {
	// if( newValue.equals( CREATE_NEW_OPTION ) )
	// {
	// dialog.setOptions( TESTCASE, new String[] { CREATE_NEW_OPTION } );
	// }
	// else
	// {
	// testSuite = getProject().getTestSuiteByName( newValue );
	// dialog.setOptions( TESTCASE, testSuite == null ? new String[] {
	// CREATE_NEW_OPTION } : ModelSupport
	// .getNames( testSuite.getTestCaseList(), new String[] { CREATE_NEW_OPTION }
	// ) );
	// }
	// }
	// } );
	// }
	//
	// String[] testSuiteNames = ModelSupport.getNames( new String[] {
	// CREATE_NEW_OPTION }, getProject()
	// .getTestSuiteList() );
	// dialog.setOptions( TESTSUITE, testSuiteNames );
	// testSuite = getProject().getTestSuiteByName( dialog.getValue( TESTSUITE )
	// );
	// dialog.setOptions( TESTCASE, testSuite == null ? new String[] {
	// CREATE_NEW_OPTION } : ModelSupport.getNames(
	// testSuite.getTestCaseList(), new String[] { CREATE_NEW_OPTION } ) );
	//
	// if( dialog.show() )
	// {
	// String targetTestSuiteName = dialog.getValue( TESTSUITE );
	// String targetTestCaseName = dialog.getValue( TESTCASE );
	//
	// WsdlTestSuite testSuite = getProject().getTestSuiteByName(
	// targetTestSuiteName );
	// if( testSuite == null )
	// {
	// targetTestSuiteName = ModelSupport.promptForUniqueName( "TestSuite",
	// getProject(), "" );
	// if( targetTestSuiteName == null )
	// return;
	//
	// testSuite = getProject().addNewTestSuite( targetTestSuiteName );
	// }
	//
	// WsdlTestCase testCase = testSuite.getTestCaseByName( targetTestCaseName );
	// if( testCase == null )
	// {
	// targetTestCaseName = ModelSupport.promptForUniqueName( "TestCase",
	// testSuite, "" );
	// if( targetTestCaseName == null )
	// return;
	//
	// testCase = testSuite.addNewTestCase( targetTestCaseName );
	// }
	//
	// for( int row : rows )
	// {
	// WsdlMonitorMessageExchange me = tableModel.getMessageExchangeAt( row );
	//
	// RestService restService = ( RestService )project.addNewInterface(
	// "Monitor Interface " + ( row + 1 ),
	// RestServiceFactory.REST_TYPE );
	// restService.addEndpoint( me.getTargetUrl().getProtocol()
	// +"://"+me.getTargetUrl().getHost() );
	// restService.setBasePath( me.getTargetUrl().getPath() );
	// ( ( NewRestResourceActionBase )SoapUI.getActionRegistry().getAction(
	// NewRestResourceAction.SOAPUI_ACTION_ID ) ).performAutomatic( restService,
	// me.getTargetUrl() );
	//
	// RestRequestStepFactory restRequestStepFactory = new
	// RestRequestStepFactory();
	// RestTestRequestStep test = ( RestTestRequestStep )testCase.insertTestStep(
	// restRequestStepFactory
	// .createNewTestStep( testCase, "Monitor Request " + ( row + 1 ) ), -1 );
	// test.getTestRequest().setRequestContent( me.getRequestContent() );
	// test.getTestRequest().setRequestHeaders( me.getRequestHeaders() );
	//
	// RestTestRequest request = ( RestTestRequest )test.getHttpRequest();
	// request.setRequestContent( me.getRequestContent() );
	// request.setEndpoint( me.getTargetUrl().toString() );
	//
	// Attachment[] requestAttachments = me.getRequestAttachments();
	// if( requestAttachments != null )
	// {
	// for( Attachment attachment : requestAttachments )
	// {
	// request.importAttachment( attachment );
	// }
	// }
	// }
	//
	// if( dialog.getBooleanValue( OPENEDITOR ) )
	// UISupport.selectAndShow( testCase );
	// }
	// }
	// }

	private final class CreateRequestsAction implements ActionListener
	{
		public void actionPerformed( ActionEvent e )
		{
			int[] rows = logTable.getSelectedRows();
			if( rows.length == 0 )
				return;

			if( UISupport.confirm( "Create " + rows.length + " requests", "Create Request" ) )
			{
				int withoutOperation = 0;
				for( int row : rows )
				{
					WsdlMonitorMessageExchange me = tableModel.getMessageExchangeAt( row );
					if( me.getOperation() == null )
					{
						withoutOperation++ ;
						continue;
					}

					WsdlRequest request = me.getOperation().addNewRequest( "Monitor Request " + ( row + 1 ) );

					request.setRequestContent( me.getRequestContent() );
					request.setEndpoint( me.getTargetUrl().toString() );

					Attachment[] requestAttachments = me.getRequestAttachments();
					if( requestAttachments != null )
					{
						for( Attachment attachment : requestAttachments )
						{
							request.importAttachment( attachment );
						}
					}
				}
				if( withoutOperation > 0 )
				{
					UISupport.showInfoMessage( "For " + withoutOperation + "request(s) there are no operations",
							"Create Request" );
				}
			}
		}
	}

	private final class ClearAction implements ActionListener
	{
		public void actionPerformed( ActionEvent e )
		{
			if( logTable.getRowCount() == 0 )
				return;

			int[] rows = logTable.getSelectedRows();

			if( rows.length == 0 )
			{
				if( UISupport.confirm( "Clear monitor log?", "Clear Log" ) )
					tableModel.clear();
			}
			else if( UISupport.confirm( "Clear " + rows.length + " rows from monitor log?", "Clear Log" ) )
			{
				tableModel.clearRows( rows );
			}
		}
	}

	@SuppressWarnings( "unchecked" )
	public class MonitorLogTableModel extends AbstractTableModel
	{
		private List<WsdlMonitorMessageExchange> exchanges = new TreeList();

		public MonitorLogTableModel()
		{
		}

		public synchronized void clear()
		{
			int sz = exchanges.size();
			while( exchanges.size() > 0 )
			{
				WsdlMonitorMessageExchange removed = exchanges.remove( 0 );
				removed.discard();
			}

			fireTableRowsDeleted( 0, sz );

			while( requestFilterModel.getSize() > 1 )
				requestFilterModel.removeElementAt( 1 );

			while( targetHostFilterModel.getSize() > 1 )
				targetHostFilterModel.removeElementAt( 1 );

			updateRowCountLabel();
		}

		public synchronized void clearRows( int[] indices )
		{
			for( int c = indices.length; c > 0; c-- )
			{
				int index = indices[c - 1];
				WsdlMonitorMessageExchange removed = exchanges.remove( logTable.convertRowIndexToModel( index ) );
				removed.discard();
				fireTableRowsDeleted( index, index );
				updateRowCountLabel();
			}
		}

		public int getColumnCount()
		{
			return 12;
		}

		public WsdlMonitorMessageExchange getMessageExchangeAt( int tableRow )
		{
			return exchanges.get( logTable.convertRowIndexToModel( tableRow ) );
		}

		@Override
		public String getColumnName( int column )
		{
			switch( column )
			{
			case 0 :
				return "Count.";
			case 1 :
				return "Time";
			case 2 :
				return "Request Host";
			case 3 :
				return "Target Host";
			case 4 :
				return "Interface";
			case 5 :
				return "Operation";
			case 6 :
				return "Time Taken";
			case 7 :
				return "Req Sz";
			case 8 :
				return "Resp Sz";
			case 9 :
				return "Method";
			case 10 :
				return "Path";
			case 11 :
				return "Content-Type";
			}

			return null;
		}

		public int getRowCount()
		{
			return exchanges.size();
		}

		public Object getValueAt( int rowIndex, int columnIndex )
		{
			if( rowIndex < 0 || rowIndex >= exchanges.size() )
				return null;

			WsdlMonitorMessageExchange exchange = exchanges.get( rowIndex );
			if( exchange == null )
				return null;

			switch( columnIndex )
			{
			case 0 :
				return rowIndex;
			case 1 :
				return DateUtil.formatFull( new Date( exchange.getTimestamp() ) );
			case 2 :
				return exchange.getRequestHost();
			case 3 :
				return exchange.getTargetUrl().getHost();
			case 4 :
				return exchange.getOperation() == null ? "- unknown -" : exchange.getOperation().getInterface().getName();
			case 5 :
				return exchange.getOperation() == null ? "- unknown -" : exchange.getOperation().getName();
			case 6 :
				return String.valueOf( exchange.getTimeTaken() );
			case 7 :
				return String.valueOf( exchange.getRequestContentLength() );
			case 8 :
				return String.valueOf( exchange.getResponseContentLength() );
			case 9 :
				return String.valueOf( exchange.getRequestMethod() );
			case 10 :
				return exchange.getTargetUrl().getPath();
			case 11 :
				return String.valueOf( exchange.getResponseContentType() );
			}

			return null;
		}

		public synchronized void addMessageExchange( WsdlMonitorMessageExchange exchange )
		{
			exchanges.add( exchange );
			int size = exchanges.size();
			fireTableRowsInserted( size - 1, size );

			fitSizeToMaxRows();

			String requestHost = exchange.getRequestHost();
			if( requestFilterModel.getIndexOf( requestHost ) == -1 )
			{
				requestFilterModel.addElement( requestHost );
			}

			String host = exchange.getTargetUrl().getHost();
			if( targetHostFilterModel.getIndexOf( host ) == -1 )
			{
				targetHostFilterModel.addElement( host );
			}

			updateRowCountLabel();
		}

		public void fitSizeToMaxRows()
		{
			int removeCnt = 0;

			while( exchanges.size() > maxRows )
			{
				WsdlMonitorMessageExchange removed = exchanges.remove( 0 );
				removed.discard();
				removeCnt++ ;
			}

			if( removeCnt > 0 )
			{
				fireTableDataChanged();
				updateRowCountLabel();
			}
		}
	}

	protected String getHttpProxyHost()
	{
		return httpProxyHost;
	}

	/**
	 * excludes proxy headers as well as headers set for excluding in Global
	 * Preferences/WebRecordingSettings.EXCLUDED_HEADERS
	 * 
	 * @param requestHeaders
	 * @return
	 */
	private StringToStringsMap excludeHeaders( StringToStringsMap requestHeaders )
	{
		StringToStringsMap stsmap = new StringToStringsMap();
		for( String key : requestHeaders.getKeys() )
		{
			if( !( key.contains( "Proxy" ) || key.contains( "Content" ) ) && !BrowserComponent.isHeaderExcluded( key ) )
			{
				stsmap.add( key, requestHeaders.get( key, "" ) );
			}
		}
		return stsmap;
	}

	protected void setHttpProxyHost( String proxyHost )
	{
		httpProxyHost = proxyHost;
	}

	protected int getHttpProxyPort()
	{
		return httpProxyPort;
	}

	protected void setHttpProxyPort( int proxyPort )
	{
		httpProxyPort = proxyPort;
	}

	// protected SlowLinkSimulator getSlowLink()
	// {
	// return slowLink;
	// }

	public String getTargetHost()
	{
		String host = targetEndpoint;

		try
		{
			URL url = new URL( host );
			return url.getHost();
		}
		catch( MalformedURLException e )
		{
			return host;
		}
	}

	public String getTargetEndpoint()
	{
		return targetEndpoint;
	}

	public int getTargetPort()
	{
		try
		{
			URL url = new URL( targetEndpoint );
			return url.getPort() == -1 ? 80 : url.getPort();
		}
		catch( MalformedURLException e )
		{
			return 80;
		}
	}

	public int getLocalPort()
	{
		return listenPort;
	}

	public synchronized void addMessageExchange( WsdlMonitorMessageExchange messageExchange )
	{
		messageExchangeStack.push( messageExchange );

		if( !stackProcessor.isRunning() )
			new Thread( stackProcessor, "SoapMonitor StackProcessor for project [" + getProject().getName() + "]" )
					.start();
	}

	private class StackProcessor implements Runnable
	{
		private boolean canceled;
		private boolean running;

		public void run()
		{
			running = true;
			SoapUI.log.info( "Started stackprocessor for soapmonitor in project [" + getProject().getName() + "]" );
			while( !canceled && messageExchangeStack.size() > 0 )
			{
				WsdlMonitorMessageExchange messageExchange = messageExchangeStack.pop();
				if( messageExchange != null )
				{
					processMessage( messageExchange );
				}

				try
				{
					Thread.sleep( 100 );
				}
				catch( InterruptedException e )
				{
					e.printStackTrace();
				}
			}
			running = false;
		}

		private synchronized void processMessage( WsdlMonitorMessageExchange messageExchange )
		{
			messageExchange.prepare( project.getWssContainer().getIncomingWssByName( incomingRequestWss ), project
					.getWssContainer().getIncomingWssByName( incomingResponseWss ) );

			tableModel.addMessageExchange( messageExchange );

			fireOnMessageExchange( messageExchange );
		}

		@SuppressWarnings( "unused" )
		public void cancel()
		{
			canceled = true;
		}

		@SuppressWarnings( "unused" )
		protected boolean isCanceled()
		{
			return canceled;
		}

		protected boolean isRunning()
		{
			return running;
		}
	}

	public MonitorLogTableModel getLogModel()
	{
		return tableModel;
	}

	public void addSoapMonitorListener( MonitorListener listener )
	{
		listeners.add( listener );
	}

	public void removeSoapMonitorListener( MonitorListener listener )
	{
		listeners.remove( listener );
	}

	public WsdlProject getProject()
	{
		return project;
	}

	public class SoapMonitorOptionsAction extends AbstractAction
	{

		public SoapMonitorOptionsAction()
		{
			putValue( SMALL_ICON, UISupport.createImageIcon( "/options.gif" ) );
		}

		public void actionPerformed( ActionEvent e )
		{
			if( optionsDialog == null )
			{
				optionsDialog = ADialogBuilder.buildDialog( OptionsForm.class );
			}

			StringList endpoints = new StringList();
			endpoints.add( null );

			for( WsdlInterface iface : ModelSupport.getChildren( getProject(), WsdlInterface.class ) )
			{
				endpoints.addAll( iface.getEndpoints() );
			}

			optionsDialog.setIntValue( OptionsForm.PORT, listenPort );
			optionsDialog.setIntValue( OptionsForm.MAXROWS, maxRows );

			optionsDialog.setOptions( OptionsForm.REQUEST_WSS,
					StringUtils.merge( project.getWssContainer().getIncomingWssNames(), "<none>" ) );
			optionsDialog.setOptions( OptionsForm.RESPONSE_WSS,
					StringUtils.merge( project.getWssContainer().getIncomingWssNames(), "<none>" ) );

			optionsDialog.setValue( OptionsForm.REQUEST_WSS, incomingRequestWss );
			optionsDialog.setValue( OptionsForm.RESPONSE_WSS, incomingResponseWss );
			optionsDialog
					.setValue(
							LaunchForm.SET_CONTENT_TYPES,
							project.getSettings().getString( LaunchForm.SET_CONTENT_TYPES,
									SoapMonitorAction.defaultContentTypes() ) );

			if( optionsDialog.show() )
			{
				Settings settings = getProject().getSettings();

				settings.setLong( OptionsForm.PORT, listenPort = optionsDialog.getIntValue( OptionsForm.PORT, listenPort ) );
				settings.setLong( OptionsForm.MAXROWS, maxRows = optionsDialog.getIntValue( OptionsForm.MAXROWS, maxRows ) );
				settings.setString( LaunchForm.SET_CONTENT_TYPES, optionsDialog.getValue( LaunchForm.SET_CONTENT_TYPES ) );

				incomingRequestWss = optionsDialog.getValue( OptionsForm.REQUEST_WSS );
				incomingResponseWss = optionsDialog.getValue( OptionsForm.RESPONSE_WSS );

				tableModel.fitSizeToMaxRows();
			}
		}

		@AForm( name = "HTTP Monitor Options", description = "Set options for HTTP Monitor", helpUrl = HelpUrls.SOAPMONITOR_HELP_URL, icon = UISupport.OPTIONS_ICON_PATH )
		private class OptionsForm
		{
			@AField( description = "The local port to listen on", name = "Port", type = AFieldType.INT )
			public final static String PORT = "Port";

			@AField( description = "The maximum number of exchanges to log", name = "Max Log", type = AFieldType.INT )
			public final static String MAXROWS = "Max Log";

			@AField( description = "The Incoming WSS configuration to use for processing requests", name = "Incoming Request WSS", type = AFieldType.ENUMERATION )
			public final static String REQUEST_WSS = "Incoming Request WSS";

			@AField( description = "The Outgoing WSS configuration to use for processing responses", name = "Incoming Response WSS", type = AFieldType.ENUMERATION )
			public final static String RESPONSE_WSS = "Incoming Response WSS";

			@AField( description = "Content types to monitor", name = "Content types to monitor", type = AFieldType.STRINGAREA )
			public final static String SET_CONTENT_TYPES = "Content types to monitor";
		}
	}

	public void release()
	{
		requestViewer.release();
		responseViewer.release();

		if( optionsDialog != null )
		{
			optionsDialog.release();
			optionsDialog = null;
		}

		inspectorPanel.release();
	}

	public boolean isRunning()
	{
		return monitorEngine.isRunning();
	}

	public void fireOnMessageExchange( WsdlMonitorMessageExchange messageExchange )
	{
		for( MonitorListener listener : listeners.get() )
		{
			try
			{
				listener.onMessageExchange( messageExchange );
			}
			catch( Throwable t )
			{
				SoapUI.logError( t );
			}
		}
	}

	public void fireOnRequest( ServletRequest request, ServletResponse response )
	{
		for( MonitorListener listener : listeners.get() )
		{
			try
			{
				listener.onRequest( this, request, response );
			}
			catch( Throwable t )
			{
				SoapUI.logError( t );
			}
		}
	}

	public void fireBeforeProxy( ServletRequest request, ServletResponse response, HttpRequest httpRequest )
	{
		for( MonitorListener listener : listeners.get() )
		{
			try
			{
				listener.beforeProxy( this, request, response, httpRequest );
			}
			catch( Throwable t )
			{
				SoapUI.logError( t );
			}
		}
	}

	public void fireAfterProxy( ServletRequest request, ServletResponse response, HttpRequest httpRequest,
			WsdlMonitorMessageExchange capturedData )
	{
		for( MonitorListener listener : listeners.get() )
		{
			try
			{
				listener.afterProxy( this, request, response, httpRequest, capturedData );
			}
			catch( Throwable t )
			{
				SoapUI.logError( t );
			}
		}
	}

	public static class SoapUIListenerSupport<T extends Object>
	{
		private Set<T> listeners = new HashSet<T>();
		@SuppressWarnings( "unused" )
		private final Class<T> listenerClass;

		public SoapUIListenerSupport( Class<T> listenerClass )
		{
			this.listenerClass = listenerClass;
			listeners.addAll( SoapUI.getListenerRegistry().getListeners( listenerClass ) );
		}

		public void add( T listener )
		{
			listeners.add( listener );
		}

		public void remove( T listener )
		{
			listeners.remove( listener );
		}

		public Collection<T> get()
		{
			return listeners;
		}
	}

	public MessageExchangeModelItem getRequestModelItem()
	{
		return requestModelItem;
	}
}
