View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2010 eviware.com
3    */
4   
5   package com.eviware.soapui.impl.wsdl.panels.teststeps.amf;
6   
7   import java.awt.BorderLayout;
8   import java.awt.Dimension;
9   import java.awt.dnd.DnDConstants;
10  import java.awt.dnd.DropTarget;
11  import java.awt.event.ActionEvent;
12  import java.awt.event.FocusAdapter;
13  import java.awt.event.FocusEvent;
14  import java.awt.event.FocusListener;
15  import java.beans.PropertyChangeEvent;
16  import java.beans.PropertyChangeListener;
17  import java.util.ArrayList;
18  import java.util.HashMap;
19  import java.util.List;
20  
21  import javax.swing.AbstractAction;
22  import javax.swing.Action;
23  import javax.swing.BorderFactory;
24  import javax.swing.Box;
25  import javax.swing.DefaultCellEditor;
26  import javax.swing.JButton;
27  import javax.swing.JComponent;
28  import javax.swing.JLabel;
29  import javax.swing.JPanel;
30  import javax.swing.JSplitPane;
31  import javax.swing.JTabbedPane;
32  import javax.swing.JTable;
33  import javax.swing.JTextField;
34  import javax.swing.JToggleButton;
35  import javax.swing.SwingUtilities;
36  import javax.swing.TransferHandler;
37  import javax.swing.event.ChangeEvent;
38  import javax.swing.event.ChangeListener;
39  import javax.swing.event.ListSelectionEvent;
40  import javax.swing.event.ListSelectionListener;
41  import javax.swing.text.Document;
42  
43  import org.apache.log4j.Logger;
44  
45  import com.eviware.soapui.SoapUI;
46  import com.eviware.soapui.config.AMFRequestTestStepConfig;
47  import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction;
48  import com.eviware.soapui.impl.support.components.ModelItemXmlEditor;
49  import com.eviware.soapui.impl.support.components.RequestMessageXmlEditor;
50  import com.eviware.soapui.impl.support.components.ResponseMessageXmlEditor;
51  import com.eviware.soapui.impl.support.panels.AbstractHttpRequestDesktopPanel;
52  import com.eviware.soapui.impl.wsdl.panels.support.TestRunComponentEnabler;
53  import com.eviware.soapui.impl.wsdl.panels.teststeps.AssertionsPanel;
54  import com.eviware.soapui.impl.wsdl.panels.teststeps.support.DefaultPropertyTableHolderModel;
55  import com.eviware.soapui.impl.wsdl.panels.teststeps.support.GroovyEditor;
56  import com.eviware.soapui.impl.wsdl.panels.teststeps.support.GroovyEditorModel;
57  import com.eviware.soapui.impl.wsdl.panels.teststeps.support.PropertyHolderTable;
58  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
59  import com.eviware.soapui.impl.wsdl.testcase.WsdlTestRunContext;
60  import com.eviware.soapui.impl.wsdl.teststeps.AMFRequestTestStep;
61  import com.eviware.soapui.impl.wsdl.teststeps.actions.AddAssertionAction;
62  import com.eviware.soapui.model.ModelItem;
63  import com.eviware.soapui.model.iface.Submit;
64  import com.eviware.soapui.model.iface.SubmitContext;
65  import com.eviware.soapui.model.iface.SubmitListener;
66  import com.eviware.soapui.model.iface.Request.SubmitException;
67  import com.eviware.soapui.model.iface.Submit.Status;
68  import com.eviware.soapui.model.settings.Settings;
69  import com.eviware.soapui.model.testsuite.Assertable;
70  import com.eviware.soapui.model.testsuite.AssertionsListener;
71  import com.eviware.soapui.model.testsuite.LoadTestRunner;
72  import com.eviware.soapui.model.testsuite.TestAssertion;
73  import com.eviware.soapui.model.testsuite.TestCaseRunner;
74  import com.eviware.soapui.model.testsuite.Assertable.AssertionStatus;
75  import com.eviware.soapui.monitor.support.TestMonitorListenerAdapter;
76  import com.eviware.soapui.settings.UISettings;
77  import com.eviware.soapui.support.DocumentListenerAdapter;
78  import com.eviware.soapui.support.StringUtils;
79  import com.eviware.soapui.support.UISupport;
80  import com.eviware.soapui.support.actions.ChangeSplitPaneOrientationAction;
81  import com.eviware.soapui.support.components.JComponentInspector;
82  import com.eviware.soapui.support.components.JEditorStatusBarWithProgress;
83  import com.eviware.soapui.support.components.JInspectorPanel;
84  import com.eviware.soapui.support.components.JInspectorPanelFactory;
85  import com.eviware.soapui.support.components.JUndoableTextField;
86  import com.eviware.soapui.support.components.JXToolBar;
87  import com.eviware.soapui.support.components.SimpleForm;
88  import com.eviware.soapui.support.editor.Editor;
89  import com.eviware.soapui.support.editor.EditorView;
90  import com.eviware.soapui.support.editor.support.AbstractEditorView;
91  import com.eviware.soapui.support.editor.xml.support.AbstractXmlDocument;
92  import com.eviware.soapui.support.propertyexpansion.PropertyExpansionPopupListener;
93  import com.eviware.soapui.support.scripting.SoapUIScriptEngine;
94  import com.eviware.soapui.support.scripting.SoapUIScriptEngineRegistry;
95  import com.eviware.soapui.support.swing.SoapUISplitPaneUI;
96  import com.eviware.soapui.ui.support.ModelItemDesktopPanel;
97  
98  @SuppressWarnings( "serial" )
99  public class AMFRequestTestStepDesktopPanel extends ModelItemDesktopPanel<AMFRequestTestStep> implements SubmitListener
100 {
101 	private static final String ENDPOINT = "Endpoint";
102 	private static final String AMF_CALL = "AMF Call";
103 	private final static Logger log = Logger.getLogger( AbstractHttpRequestDesktopPanel.class );
104 	private JPanel configPanel;
105 	private JButton addAssertionButton;
106 	private JInspectorPanel inspectorPanel;
107 	private AMFRequestTestStep amfRequestTestStep;
108 	protected AMFRequestTestStepConfig amfRequestTestStepConfig;
109 	private JComponentInspector<?> assertionInspector;
110 	private AssertionsPanel assertionsPanel;
111 	private InternalAssertionsListener assertionsListener = new InternalAssertionsListener();
112 	private InternalTestMonitorListener testMonitorListener = new InternalTestMonitorListener();
113 	private JComponent requestEditor;
114 	private ModelItemXmlEditor<?, ?> responseEditor;
115 	private Submit submit;
116 	private JButton submitButton;
117 	private JToggleButton tabsButton;
118 	private JTabbedPane requestTabs;
119 	private JPanel requestTabPanel;
120 	private boolean responseHasFocus;
121 	private JSplitPane requestSplitPane;
122 	private JEditorStatusBarWithProgress statusBar;
123 	private JButton cancelButton;
124 	private JButton splitButton;
125 	private JComponent propertiesTableComponent;
126 	private SoapUIScriptEngine scriptEngine;
127 	private RunAction runAction = new RunAction();
128 	private GroovyEditor groovyEditor;
129 	private JTextField amfCallField;
130 	public boolean updating;
131 	SimpleForm configForm;
132 	private JTextField endpointField;
133 	private TestRunComponentEnabler componentEnabler;
134 	protected PropertyHolderTable propertyHolderTable;
135 
136 	public AMFRequestTestStepDesktopPanel( AMFRequestTestStep modelItem )
137 	{
138 		super( modelItem );
139 		amfRequestTestStep = modelItem;
140 		componentEnabler = new TestRunComponentEnabler( amfRequestTestStep.getTestCase() );
141 		initConfig();
142 		initContent();
143 
144 		SoapUI.getTestMonitor().addTestMonitorListener( testMonitorListener );
145 		setEnabled( !SoapUI.getTestMonitor().hasRunningTest( amfRequestTestStep.getTestCase() ) );
146 
147 		amfRequestTestStep.addAssertionsListener( assertionsListener );
148 
149 		scriptEngine = SoapUIScriptEngineRegistry.create( modelItem );
150 		scriptEngine.setScript( amfRequestTestStep.getScript() );
151 
152 	}
153 
154 	protected void initConfig()
155 	{
156 		amfRequestTestStepConfig = amfRequestTestStep.getAMFRequestTestStepConfig();
157 	}
158 
159 	private JComponent buildContent()
160 	{
161 		requestSplitPane = UISupport.createHorizontalSplit();
162 		requestSplitPane.setResizeWeight( 0.5 );
163 		requestSplitPane.setBorder( null );
164 
165 		JComponent content;
166 		submitButton = createActionButton( new SubmitAction(), true );
167 		submitButton.setEnabled( enableSubmit() );
168 		cancelButton = createActionButton( new CancelAction(), false );
169 		tabsButton = new JToggleButton( new ChangeToTabsAction() );
170 		tabsButton.setPreferredSize( UISupport.TOOLBAR_BUTTON_DIMENSION );
171 		splitButton = createActionButton( new ChangeSplitPaneOrientationAction( requestSplitPane ), true );
172 
173 		addAssertionButton = UISupport.createToolbarButton( new AddAssertionAction( amfRequestTestStep ) );
174 		addAssertionButton.setEnabled( true );
175 
176 		requestTabs = new JTabbedPane();
177 		requestTabs.addChangeListener( new ChangeListener()
178 		{
179 
180 			public void stateChanged( ChangeEvent e )
181 			{
182 				SwingUtilities.invokeLater( new Runnable()
183 				{
184 
185 					public void run()
186 					{
187 						int ix = requestTabs.getSelectedIndex();
188 						if( ix == 0 )
189 							requestEditor.requestFocus();
190 						else if( ix == 1 && responseEditor != null )
191 							responseEditor.requestFocus();
192 					}
193 				} );
194 			}
195 		} );
196 
197 		addFocusListener( new FocusAdapter()
198 		{
199 
200 			@Override
201 			public void focusGained( FocusEvent e )
202 			{
203 				if( requestTabs.getSelectedIndex() == 1 || responseHasFocus )
204 					responseEditor.requestFocusInWindow();
205 				else
206 					requestEditor.requestFocusInWindow();
207 			}
208 		} );
209 
210 		requestTabPanel = UISupport.createTabPanel( requestTabs, true );
211 
212 		requestEditor = ( JComponent )buildRequestConfigPanel();
213 		responseEditor = buildResponseEditor();
214 		if( amfRequestTestStep.getSettings().getBoolean( UISettings.START_WITH_REQUEST_TABS ) )
215 		{
216 			requestTabs.addTab( "Request", requestEditor );
217 			if( responseEditor != null )
218 				requestTabs.addTab( "Response", responseEditor );
219 			tabsButton.setSelected( true );
220 			splitButton.setEnabled( false );
221 
222 			content = requestTabPanel;
223 		}
224 		else
225 		{
226 			requestSplitPane.setTopComponent( requestEditor );
227 			requestSplitPane.setBottomComponent( responseEditor );
228 			requestSplitPane.setDividerLocation( 0.5 );
229 			content = requestSplitPane;
230 		}
231 
232 		inspectorPanel = JInspectorPanelFactory.build( content );
233 		inspectorPanel.setDefaultDividerLocation( 0.7F );
234 		add( buildToolbar(), BorderLayout.NORTH );
235 		add( inspectorPanel.getComponent(), BorderLayout.CENTER );
236 		assertionsPanel = buildAssertionsPanel();
237 
238 		assertionInspector = new JComponentInspector<JComponent>( assertionsPanel, "Assertions ("
239 				+ getModelItem().getAssertionCount() + ")", "Assertions for this Test Request", true );
240 
241 		inspectorPanel.addInspector( assertionInspector );
242 		// setPreferredSize(new Dimension(600, 450));
243 
244 		updateStatusIcon();
245 
246 		return inspectorPanel.getComponent();
247 	}
248 
249 	@SuppressWarnings( "unchecked" )
250 	protected JComponent buildRequestConfigPanel()
251 	{
252 		ModelItemXmlEditor<?, ?> reqEditor = buildRequestEditor();
253 
254 		configPanel = UISupport.addTitledBorder( new JPanel( new BorderLayout() ), "Script" );
255 		configPanel.add( buildToolbarButtonAndText(), BorderLayout.NORTH );
256 		groovyEditor = ( GroovyEditor )UISupport.getEditorFactory().buildGroovyEditor( new ScriptStepGroovyEditorModel() );
257 		configPanel.add( groovyEditor, BorderLayout.CENTER );
258 		propertiesTableComponent = buildProperties();
259 		final JSplitPane split = UISupport.createVerticalSplit( propertiesTableComponent, configPanel );
260 		split.setDividerLocation( 120 );
261 		reqEditor.addEditorView( ( EditorView )new AbstractEditorView<AMFRequestDocument>( "AMF",
262 				( Editor<AMFRequestDocument> )reqEditor, "amf" )
263 		{
264 			@Override
265 			public JComponent buildUI()
266 			{
267 				return split;
268 			}
269 		} );
270 		reqEditor.selectView( 1 );
271 		return reqEditor;
272 	}
273 
274 	private JComponent buildToolbarButtonAndText()
275 	{
276 		JXToolBar toolBar = UISupport.createToolbar();
277 		JButton runButton = UISupport.createToolbarButton( runAction );
278 		toolBar.add( runButton );
279 		toolBar.add( Box.createHorizontalGlue() );
280 		JLabel label = new JLabel( "<html>Script is invoked with <code>log</code>, <code>context</code> "
281 				+ ", <code>parameters</code> and <code>amfHeaders</code> variables</html>" );
282 		label.setToolTipText( label.getText() );
283 		label.setMinimumSize( label.getPreferredSize() );
284 		label.setMaximumSize( label.getPreferredSize() );
285 
286 		toolBar.add( label );
287 		toolBar.addRelatedGap();
288 		toolBar.add( UISupport.createToolbarButton( new ShowOnlineHelpAction( HelpUrls.GROOVYSTEPEDITOR_HELP_URL ) ) );
289 
290 		componentEnabler.add( runButton );
291 
292 		return toolBar;
293 	}
294 
295 	protected JComponent buildToolbar()
296 	{
297 
298 		JPanel panel = new JPanel( new BorderLayout() );
299 		panel.add( buildToolbar1(), BorderLayout.NORTH );
300 		panel.add( buildToolbar2(), BorderLayout.SOUTH );
301 		return panel;
302 
303 	}
304 
305 	protected void initContent()
306 	{
307 		amfRequestTestStep.getAMFRequest().addSubmitListener( this );
308 
309 		add( buildContent(), BorderLayout.CENTER );
310 		add( buildToolbar(), BorderLayout.NORTH );
311 		add( buildStatusLabel(), BorderLayout.SOUTH );
312 
313 		setPreferredSize( new Dimension( 600, 500 ) );
314 
315 		addFocusListener( new FocusAdapter()
316 		{
317 
318 			@Override
319 			public void focusGained( FocusEvent e )
320 			{
321 				if( requestTabs.getSelectedIndex() == 1 || responseHasFocus )
322 					responseEditor.requestFocusInWindow();
323 				else
324 					requestEditor.requestFocusInWindow();
325 			}
326 		} );
327 	}
328 
329 	protected JComponent buildStatusLabel()
330 	{
331 		statusBar = new JEditorStatusBarWithProgress();
332 		statusBar.setBorder( BorderFactory.createEmptyBorder( 1, 0, 0, 0 ) );
333 
334 		return statusBar;
335 	}
336 
337 	protected JComponent buildProperties()
338 	{
339 		propertyHolderTable = new PropertyHolderTable( getModelItem() )
340 		{
341 			protected JTable buildPropertiesTable()
342 			{
343 				propertiesModel = new DefaultPropertyTableHolderModel( holder )
344 				{
345 					@Override
346 					public String[] getPropertyNames()
347 					{
348 						List<String> propertyNamesList = new ArrayList<String>();
349 						for( String name : holder.getPropertyNames() )
350 						{
351 							if( name.equals( "ResponseAsXML" ) )
352 							{
353 								continue;
354 							}
355 							propertyNamesList.add( name );
356 						}
357 						return propertyNamesList.toArray( new String[propertyNamesList.size()] );
358 					}
359 				};
360 				propertiesTable = new PropertiesHolderJTable();
361 				propertiesTable.setSurrendersFocusOnKeystroke( true );
362 
363 				propertiesTable.putClientProperty( "terminateEditOnFocusLost", Boolean.TRUE );
364 				propertiesTable.getSelectionModel().addListSelectionListener( new ListSelectionListener()
365 				{
366 					public void valueChanged( ListSelectionEvent e )
367 					{
368 						int selectedRow = propertiesTable.getSelectedRow();
369 						if( removePropertyAction != null )
370 							removePropertyAction.setEnabled( selectedRow != -1 );
371 
372 						if( movePropertyUpAction != null )
373 							movePropertyUpAction.setEnabled( selectedRow > 0 );
374 
375 						if( movePropertyDownAction != null )
376 							movePropertyDownAction.setEnabled( selectedRow >= 0
377 									&& selectedRow < propertiesTable.getRowCount() - 1 );
378 					}
379 				} );
380 
381 				propertiesTable.setDragEnabled( true );
382 				propertiesTable.setTransferHandler( new TransferHandler( "testProperty" ) );
383 
384 				if( getHolder().getModelItem() != null )
385 				{
386 					DropTarget dropTarget = new DropTarget( propertiesTable,
387 							new PropertyHolderTablePropertyExpansionDropTarget() );
388 					dropTarget.setDefaultActions( DnDConstants.ACTION_COPY_OR_MOVE );
389 				}
390 
391 				return propertiesTable;
392 			}
393 		};
394 
395 		JUndoableTextField textField = new JUndoableTextField( true );
396 
397 		PropertyExpansionPopupListener.enable( textField, getModelItem() );
398 		propertyHolderTable.getPropertiesTable().setDefaultEditor( String.class, new DefaultCellEditor( textField ) );
399 
400 		return propertyHolderTable;
401 	}
402 
403 	protected JComponent buildToolbar1()
404 	{
405 		JXToolBar toolbar = UISupport.createToolbar();
406 
407 		toolbar.setBorder( BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) );
408 
409 		toolbar.addFixed( submitButton );
410 		toolbar.add( cancelButton );
411 		toolbar.addFixed( addAssertionButton );
412 
413 		toolbar.add( Box.createHorizontalGlue() );
414 		toolbar.add( tabsButton );
415 		toolbar.add( splitButton );
416 
417 		toolbar.addFixed( UISupport
418 				.createToolbarButton( new ShowOnlineHelpAction( HelpUrls.TRANSFERSTEPEDITOR_HELP_URL ) ) );
419 
420 		return toolbar;
421 
422 	}
423 
424 	protected JComponent buildToolbar2()
425 	{
426 		JXToolBar toolbar = UISupport.createToolbar();
427 
428 		toolbar.setBorder( BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) );
429 
430 		toolbar.addLabeledFixed( ENDPOINT, addEndpointField() );
431 		toolbar.addSeparator();
432 		toolbar.addLabeledFixed( AMF_CALL, addAmfCallField() );
433 
434 		return toolbar;
435 
436 	}
437 
438 	public AMFRequestTestStep getAMFRequestTestStep()
439 	{
440 		return amfRequestTestStep;
441 	}
442 
443 	protected AssertionsPanel buildAssertionsPanel()
444 	{
445 		return new AMFAssertionsPanel( amfRequestTestStep )
446 		{
447 		};
448 	}
449 
450 	protected class AMFAssertionsPanel extends AssertionsPanel
451 	{
452 		public AMFAssertionsPanel( Assertable assertable )
453 		{
454 			super( assertable );
455 		}
456 	}
457 
458 	private JTextField addAmfCallField()
459 	{
460 		amfCallField = new JTextField();
461 		amfCallField.setText( amfRequestTestStep.getAmfCall() );
462 		amfCallField.setColumns( 20 );
463 		amfCallField.setToolTipText( "object.methodName for amf method call" );
464 		PropertyExpansionPopupListener.enable( amfCallField, amfRequestTestStep );
465 		addAmfCallDocumentListener();
466 		return amfCallField;
467 	}
468 
469 	private JTextField addEndpointField()
470 	{
471 		endpointField = new JTextField();
472 		endpointField.setText( amfRequestTestStep.getEndpoint() );
473 		endpointField.setColumns( 35 );
474 		endpointField.setToolTipText( "http to connect" );
475 		PropertyExpansionPopupListener.enable( endpointField, amfRequestTestStep );
476 		addEndpointCallDocumentListener();
477 		return endpointField;
478 	}
479 
480 	protected void addAmfCallDocumentListener()
481 	{
482 		amfCallField.getDocument().addDocumentListener( new DocumentListenerAdapter()
483 		{
484 			@Override
485 			public void update( Document document )
486 			{
487 				if( !updating )
488 				{
489 					amfRequestTestStep.setAmfCall( amfCallField.getText() );
490 				}
491 			}
492 		} );
493 	}
494 
495 	protected void addEndpointCallDocumentListener()
496 	{
497 		endpointField.getDocument().addDocumentListener( new DocumentListenerAdapter()
498 		{
499 			@Override
500 			public void update( Document document )
501 			{
502 				if( !updating )
503 				{
504 					amfRequestTestStep.setEndpoint( endpointField.getText() );
505 				}
506 			}
507 		} );
508 	}
509 
510 	protected boolean enableSubmit()
511 	{
512 		return !StringUtils.isNullOrEmpty( amfRequestTestStep.getEndpoint() )
513 				&& !StringUtils.isNullOrEmpty( amfRequestTestStep.getAmfCall() );
514 	}
515 
516 	private class ScriptStepGroovyEditorModel implements GroovyEditorModel
517 	{
518 		public String[] getKeywords()
519 		{
520 			return new String[] { "log", "context", "property" };
521 		}
522 
523 		public Action getRunAction()
524 		{
525 			return runAction;
526 		}
527 
528 		public String getScript()
529 		{
530 			return amfRequestTestStep.getScript();
531 		}
532 
533 		public void setScript( String text )
534 		{
535 			if( updating )
536 				return;
537 
538 			updating = true;
539 			amfRequestTestStep.setScript( text );
540 			updating = false;
541 		}
542 
543 		public Settings getSettings()
544 		{
545 			return SoapUI.getSettings();
546 		}
547 
548 		public String getScriptName()
549 		{
550 			return null;
551 		}
552 
553 		public void addPropertyChangeListener( PropertyChangeListener listener )
554 		{
555 		}
556 
557 		public void removePropertyChangeListener( PropertyChangeListener listener )
558 		{
559 		}
560 
561 		public ModelItem getModelItem()
562 		{
563 			return amfRequestTestStep;
564 		}
565 	}
566 
567 	private class RunAction extends AbstractAction
568 	{
569 		public RunAction()
570 		{
571 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/run_groovy_script.gif" ) );
572 			putValue( Action.SHORT_DESCRIPTION,
573 					"Runs this script in a seperate thread using a mock testRunner and testContext" );
574 		}
575 
576 		public void actionPerformed( ActionEvent e )
577 		{
578 			SoapUI.getThreadPool().execute( new Runnable()
579 			{
580 				public void run()
581 				{
582 					SubmitContext context = new WsdlTestRunContext( getModelItem() );
583 					statusBar.setIndeterminate( true );
584 					amfRequestTestStep.initAmfRequest( context );
585 
586 					if( context.getProperty( AMFRequest.AMF_SCRIPT_ERROR ) != null )
587 					{
588 						UISupport.showInfoMessage( ( ( Throwable )context.getProperty( AMFRequest.AMF_SCRIPT_ERROR ) )
589 								.getMessage() );
590 					}
591 					else
592 					{
593 						UISupport.showInfoMessage( scriptInfo( context ) );
594 
595 					}
596 					statusBar.setIndeterminate( false );
597 					amfRequestTestStep.getAMFRequest().clearArguments();
598 				}
599 
600 				@SuppressWarnings( "unchecked" )
601 				private String scriptInfo( SubmitContext context )
602 				{
603 					HashMap<String, Object> parameters = ( HashMap<String, Object> )context
604 							.getProperty( AMFRequest.AMF_SCRIPT_PARAMETERS );
605 					HashMap<String, Object> amfHeaders = ( HashMap<String, Object> )context
606 							.getProperty( AMFRequest.AMF_SCRIPT_HEADERS );
607 					StringBuilder sb = new StringBuilder();
608 					sb.append( "parameters " + ( parameters != null ? parameters.toString() : "" ) );
609 					sb.append( "\n" );
610 					sb.append( "amfHeaders " + ( amfHeaders != null ? amfHeaders.toString() : "" ) );
611 					return sb.toString();
612 				}
613 			} );
614 		}
615 
616 	}
617 
618 	protected ModelItemXmlEditor<?, ?> buildResponseEditor()
619 	{
620 		return new AMFResponseMessageEditor();
621 	}
622 
623 	protected ModelItemXmlEditor<?, ?> buildRequestEditor()
624 	{
625 		return new AMFRequestMessageEditor();
626 	}
627 
628 	public class AMFResponseMessageEditor extends ResponseMessageXmlEditor<AMFRequestTestStep, AMFResponseDocument>
629 	{
630 		public AMFResponseMessageEditor()
631 		{
632 			super( new AMFResponseDocument(), amfRequestTestStep );
633 		}
634 
635 		@Override
636 		public void release()
637 		{
638 			getDocument().release();
639 			super.release();
640 		}
641 	}
642 
643 	public class AMFRequestMessageEditor extends RequestMessageXmlEditor<AMFRequestTestStep, AMFRequestDocument>
644 	{
645 		public AMFRequestMessageEditor()
646 		{
647 			super( new AMFRequestDocument(), amfRequestTestStep );
648 		}
649 
650 		@Override
651 		public void release()
652 		{
653 			getDocument().release();
654 			super.release();
655 		}
656 
657 	}
658 
659 	public boolean dependsOn( ModelItem modelItem )
660 	{
661 		return modelItem == getModelItem() || modelItem == getModelItem().getTestCase()
662 				|| modelItem == getModelItem().getTestCase().getTestSuite()
663 				|| modelItem == getModelItem().getTestCase().getTestSuite().getProject();
664 	}
665 
666 	public boolean onClose( boolean canCancel )
667 	{
668 		configPanel.removeAll();
669 		inspectorPanel.release();
670 
671 		requestEditor.removeAll();
672 		( ( ModelItemXmlEditor<?, ?> )requestEditor ).release();
673 		responseEditor.release();
674 		responseEditor.removeAll();
675 		responseEditor = null;
676 		assertionsPanel.release();
677 		SoapUI.getTestMonitor().removeTestMonitorListener( testMonitorListener );
678 		amfRequestTestStep.removeAssertionsListener( assertionsListener );
679 		amfRequestTestStep.getAMFRequest().removeSubmitListener( this );
680 		componentEnabler.release();
681 		groovyEditor.release();
682 		amfRequestTestStep.release();
683 		propertyHolderTable.release();
684 		this.removeAll();
685 		return release();
686 	}
687 
688 	public class AMFResponseDocument extends AbstractXmlDocument implements PropertyChangeListener
689 	{
690 		public AMFResponseDocument()
691 		{
692 			amfRequestTestStep.addPropertyChangeListener( AMFRequestTestStep.RESPONSE_PROPERTY, this );
693 		}
694 
695 		public void propertyChange( PropertyChangeEvent evt )
696 		{
697 			fireXmlChanged( evt.getOldValue() == null ? null : ( ( AMFResponse )evt.getOldValue() ).getContentAsString(),
698 					getXml() );
699 		}
700 
701 		public String getXml()
702 		{
703 			AMFResponse response = amfRequestTestStep.getAMFRequest().getResponse();
704 			return response == null ? null : response.getResponseContentXML();
705 		}
706 
707 		public void setXml( String xml )
708 		{
709 			if( amfRequestTestStep.getAMFRequest().getResponse() != null )
710 				amfRequestTestStep.getAMFRequest().getResponse().setResponseContentXML( xml );
711 		}
712 
713 		public void release()
714 		{
715 			super.release();
716 			amfRequestTestStep.removePropertyChangeListener( AMFRequestTestStep.RESPONSE_PROPERTY, this );
717 		}
718 	}
719 
720 	public class AMFRequestDocument extends AbstractXmlDocument implements PropertyChangeListener
721 	{
722 		public AMFRequestDocument()
723 		{
724 			amfRequestTestStep.addPropertyChangeListener( AMFRequestTestStep.REQUEST_PROPERTY, this );
725 		}
726 
727 		public void propertyChange( PropertyChangeEvent evt )
728 		{
729 			fireXmlChanged( evt.getOldValue() == null ? null : ( ( AMFRequest )evt.getOldValue() ).requestAsXML(),
730 					getXml() );
731 		}
732 
733 		public String getXml()
734 		{
735 			AMFRequest request = amfRequestTestStep.getAMFRequest();
736 			return request == null ? null : request.requestAsXML();
737 		}
738 
739 		public void setXml( String xml )
740 		{
741 		}
742 
743 		public void release()
744 		{
745 			super.release();
746 			amfRequestTestStep.removePropertyChangeListener( AMFRequestTestStep.REQUEST_PROPERTY, this );
747 		}
748 	}
749 
750 	private class InternalTestMonitorListener extends TestMonitorListenerAdapter
751 	{
752 		public void loadTestFinished( LoadTestRunner runner )
753 		{
754 			setEnabled( !SoapUI.getTestMonitor().hasRunningTest( getModelItem().getTestCase() ) );
755 		}
756 
757 		public void loadTestStarted( LoadTestRunner runner )
758 		{
759 			if( runner.getLoadTest().getTestCase() == getModelItem().getTestCase() )
760 				setEnabled( false );
761 		}
762 
763 		public void testCaseFinished( TestCaseRunner runner )
764 		{
765 			setEnabled( !SoapUI.getTestMonitor().hasRunningTest( getModelItem().getTestCase() ) );
766 		}
767 
768 		public void testCaseStarted( TestCaseRunner runner )
769 		{
770 			if( runner.getTestCase() == getModelItem().getTestCase() )
771 				setEnabled( false );
772 		}
773 	}
774 
775 	public class SubmitAction extends AbstractAction
776 	{
777 		public SubmitAction()
778 		{
779 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/submit_request.gif" ) );
780 			putValue( Action.SHORT_DESCRIPTION, "Submit request to specified endpoint URL" );
781 			putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "alt ENTER" ) );
782 		}
783 
784 		public void actionPerformed( ActionEvent e )
785 		{
786 			onSubmit();
787 		}
788 	}
789 
790 	protected void onSubmit()
791 	{
792 		if( submit != null && submit.getStatus() == Submit.Status.RUNNING )
793 		{
794 			if( UISupport.confirm( "Cancel current request?", "Submit Request" ) )
795 			{
796 				submit.cancel();
797 			}
798 			else
799 				return;
800 		}
801 
802 		try
803 		{
804 			submit = doSubmit();
805 		}
806 		catch( SubmitException e1 )
807 		{
808 			SoapUI.logError( e1 );
809 		}
810 	}
811 
812 	protected Submit doSubmit() throws SubmitException
813 	{
814 
815 		SubmitContext submitContext = new WsdlTestRunContext( getModelItem() );
816 		if( !amfRequestTestStep.initAmfRequest( submitContext ) )
817 		{
818 			throw new SubmitException( "AMF request is not initialised properly !" );
819 		}
820 
821 		return amfRequestTestStep.getAMFRequest().submit( submitContext, true );
822 	}
823 
824 	protected final class InputAreaFocusListener implements FocusListener
825 	{
826 		public InputAreaFocusListener( JComponent editor )
827 		{
828 		}
829 
830 		public void focusGained( FocusEvent e )
831 		{
832 			responseHasFocus = false;
833 
834 			// statusBar.setTarget(sourceEditor.getInputArea());
835 			if( !splitButton.isEnabled() )
836 			{
837 				requestTabs.setSelectedIndex( 0 );
838 				return;
839 			}
840 
841 			// if
842 			// (getModelItem().getSettings().getBoolean(UISettings.NO_RESIZE_REQUEST_EDITOR))
843 			// return;
844 
845 			// // dont resize if split has been dragged
846 			// if (requestSplitPane.getUI() instanceof SoapUISplitPaneUI
847 			// && ((SoapUISplitPaneUI) requestSplitPane.getUI()).hasBeenDragged())
848 			// return;
849 			//
850 			int pos = requestSplitPane.getDividerLocation();
851 			if( pos >= 600 )
852 				return;
853 			if( requestSplitPane.getMaximumDividerLocation() > 700 )
854 				requestSplitPane.setDividerLocation( 600 );
855 			else
856 				requestSplitPane.setDividerLocation( 0.8 );
857 		}
858 
859 		public void focusLost( FocusEvent e )
860 		{
861 		}
862 	}
863 
864 	protected final class ResultAreaFocusListener implements FocusListener
865 	{
866 		@SuppressWarnings( "unused" )
867 		private final ModelItemXmlEditor<?, ?> responseEditor;
868 
869 		public ResultAreaFocusListener( ModelItemXmlEditor<?, ?> editor )
870 		{
871 			this.responseEditor = editor;
872 		}
873 
874 		public void focusGained( FocusEvent e )
875 		{
876 			responseHasFocus = true;
877 
878 			// statusBar.setTarget(sourceEditor.getInputArea());
879 			if( !splitButton.isEnabled() )
880 			{
881 				requestTabs.setSelectedIndex( 1 );
882 				return;
883 			}
884 
885 			if( getModelItem().getSettings().getBoolean( UISettings.NO_RESIZE_REQUEST_EDITOR ) )
886 				return;
887 
888 			// dont resize if split has been dragged or result is empty
889 			if( requestSplitPane.getUI() instanceof SoapUISplitPaneUI
890 					&& ( ( SoapUISplitPaneUI )requestSplitPane.getUI() ).hasBeenDragged()
891 					|| amfRequestTestStep.getAMFRequest().getResponse() == null )
892 				return;
893 
894 			int pos = requestSplitPane.getDividerLocation();
895 			int maximumDividerLocation = requestSplitPane.getMaximumDividerLocation();
896 			if( pos + 600 < maximumDividerLocation )
897 				return;
898 
899 			if( maximumDividerLocation > 700 )
900 				requestSplitPane.setDividerLocation( maximumDividerLocation - 600 );
901 			else
902 				requestSplitPane.setDividerLocation( 0.2 );
903 		}
904 
905 		public void focusLost( FocusEvent e )
906 		{
907 		}
908 	}
909 
910 	private final class ChangeToTabsAction extends AbstractAction
911 	{
912 		public ChangeToTabsAction()
913 		{
914 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/toggle_tabs.gif" ) );
915 			putValue( Action.SHORT_DESCRIPTION, "Toggles to tab-based layout" );
916 		}
917 
918 		public void actionPerformed( ActionEvent e )
919 		{
920 			if( splitButton.isEnabled() )
921 			{
922 				splitButton.setEnabled( false );
923 				removeContent( requestSplitPane );
924 				setContent( requestTabPanel );
925 				requestTabs.addTab( "Request", requestEditor );
926 
927 				if( responseEditor != null )
928 					requestTabs.addTab( "Response", responseEditor );
929 
930 				if( responseHasFocus )
931 				{
932 					requestTabs.setSelectedIndex( 1 );
933 					requestEditor.requestFocus();
934 				}
935 				requestTabs.repaint();
936 			}
937 			else
938 			{
939 				int selectedIndex = requestTabs.getSelectedIndex();
940 
941 				splitButton.setEnabled( true );
942 				removeContent( requestTabPanel );
943 				setContent( requestSplitPane );
944 				requestSplitPane.setTopComponent( requestEditor );
945 				if( responseEditor != null )
946 					requestSplitPane.setBottomComponent( responseEditor );
947 				requestSplitPane.setDividerLocation( 0.5 );
948 
949 				if( selectedIndex == 0 || responseEditor == null )
950 					requestEditor.requestFocus();
951 				else
952 					responseEditor.requestFocus();
953 				requestSplitPane.repaint();
954 			}
955 
956 			revalidate();
957 		}
958 	}
959 
960 	public void setContent( JComponent content )
961 	{
962 		inspectorPanel.setContentComponent( content );
963 	}
964 
965 	public void removeContent( JComponent content )
966 	{
967 		inspectorPanel.setContentComponent( null );
968 	}
969 
970 	private class CancelAction extends AbstractAction
971 	{
972 		public CancelAction()
973 		{
974 			super();
975 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/cancel_request.gif" ) );
976 			putValue( Action.SHORT_DESCRIPTION, "Aborts ongoing request" );
977 			putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "alt X" ) );
978 		}
979 
980 		public void actionPerformed( ActionEvent e )
981 		{
982 			onCancel();
983 		}
984 	}
985 
986 	protected void onCancel()
987 	{
988 		if( submit == null )
989 			return;
990 
991 		cancelButton.setEnabled( false );
992 		submit.cancel();
993 		setEnabled( true );
994 		submit = null;
995 	}
996 
997 	public void setEnabled( boolean enabled )
998 	{
999 		if( responseEditor != null )
1000 			responseEditor.setEnabled( enabled );
1001 
1002 		submitButton.setEnabled( enabled );
1003 		addAssertionButton.setEnabled( enabled );
1004 		propertiesTableComponent.setEnabled( enabled );
1005 		groovyEditor.setEnabled( enabled );
1006 		endpointField.setEnabled( enabled );
1007 		amfCallField.setEnabled( enabled );
1008 
1009 		statusBar.setIndeterminate( !enabled );
1010 	}
1011 
1012 	public void afterSubmit( Submit submit, SubmitContext context )
1013 	{
1014 		if( submit.getRequest() != amfRequestTestStep.getAMFRequest() )
1015 			return;
1016 
1017 		Status status = submit.getStatus();
1018 		AMFResponse response = ( AMFResponse )submit.getResponse();
1019 		if( status == Status.FINISHED )
1020 		{
1021 			amfRequestTestStep.setResponse( response, context );
1022 		}
1023 
1024 		cancelButton.setEnabled( false );
1025 		setEnabled( true );
1026 
1027 		String message = null;
1028 		String infoMessage = null;
1029 		String requestName = amfRequestTestStep.getName();
1030 
1031 		if( status == Status.CANCELED )
1032 		{
1033 			message = "CANCELED";
1034 			infoMessage = "[" + requestName + "] - CANCELED";
1035 		}
1036 		else
1037 		{
1038 			if( status == Status.ERROR || response == null )
1039 			{
1040 				message = "Error getting response; " + submit.getError();
1041 				infoMessage = "Error getting response for [" + requestName + "]; " + submit.getError();
1042 			}
1043 			else
1044 			{
1045 				message = "response time: " + response.getTimeTaken() + "ms (" + response.getContentLength() + " bytes)";
1046 				infoMessage = "Got response for [" + requestName + "] in " + response.getTimeTaken() + "ms ("
1047 						+ response.getContentLength() + " bytes)";
1048 
1049 				if( !splitButton.isEnabled() )
1050 					requestTabs.setSelectedIndex( 1 );
1051 
1052 				responseEditor.requestFocus();
1053 			}
1054 		}
1055 
1056 		logMessages( message, infoMessage );
1057 
1058 		if( getModelItem().getSettings().getBoolean( UISettings.AUTO_VALIDATE_RESPONSE ) )
1059 			responseEditor.getSourceEditor().validate();
1060 
1061 		AMFRequestTestStepDesktopPanel.this.submit = null;
1062 
1063 		updateStatusIcon();
1064 	}
1065 
1066 	protected void logMessages( String message, String infoMessage )
1067 	{
1068 		log.info( infoMessage );
1069 		statusBar.setInfo( message );
1070 	}
1071 
1072 	public boolean beforeSubmit( Submit submit, SubmitContext context )
1073 	{
1074 		if( submit.getRequest() != amfRequestTestStep.getAMFRequest() )
1075 			return true;
1076 
1077 		setEnabled( false );
1078 		cancelButton.setEnabled( AMFRequestTestStepDesktopPanel.this.submit != null );
1079 		return true;
1080 	}
1081 
1082 	public void propertyChange( PropertyChangeEvent evt )
1083 	{
1084 		super.propertyChange( evt );
1085 		if( evt.getPropertyName().equals( "script" ) && !updating )
1086 		{
1087 			updating = true;
1088 			groovyEditor.getEditArea().setText( ( String )evt.getNewValue() );
1089 			updating = false;
1090 		}
1091 		if( evt.getPropertyName().equals( AMFRequestTestStep.STATUS_PROPERTY ) )
1092 			updateStatusIcon();
1093 	}
1094 
1095 	private final class InternalAssertionsListener implements AssertionsListener
1096 	{
1097 		public void assertionAdded( TestAssertion assertion )
1098 		{
1099 			assertionInspector.setTitle( "Assertions (" + getModelItem().getAssertionCount() + ")" );
1100 		}
1101 
1102 		public void assertionRemoved( TestAssertion assertion )
1103 		{
1104 			assertionInspector.setTitle( "Assertions (" + getModelItem().getAssertionCount() + ")" );
1105 		}
1106 
1107 		public void assertionMoved( TestAssertion assertion, int ix, int offset )
1108 		{
1109 			assertionInspector.setTitle( "Assertions (" + getModelItem().getAssertionCount() + ")" );
1110 		}
1111 	}
1112 
1113 	private void updateStatusIcon()
1114 	{
1115 		AssertionStatus status = amfRequestTestStep.getAssertionStatus();
1116 		switch( status )
1117 		{
1118 		case FAILED :
1119 		{
1120 			assertionInspector.setIcon( UISupport.createImageIcon( "/failed_assertion.gif" ) );
1121 			inspectorPanel.activate( assertionInspector );
1122 			break;
1123 		}
1124 		case UNKNOWN :
1125 		{
1126 			assertionInspector.setIcon( UISupport.createImageIcon( "/unknown_assertion.gif" ) );
1127 			break;
1128 		}
1129 		case VALID :
1130 		{
1131 			assertionInspector.setIcon( UISupport.createImageIcon( "/valid_assertion.gif" ) );
1132 			inspectorPanel.deactivate();
1133 			break;
1134 		}
1135 		}
1136 	}
1137 
1138 }