View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2007 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.panels.request;
14  
15  import java.awt.BorderLayout;
16  import java.awt.Component;
17  import java.awt.Dimension;
18  import java.awt.event.ActionEvent;
19  import java.awt.event.FocusAdapter;
20  import java.awt.event.FocusEvent;
21  import java.awt.event.FocusListener;
22  import java.awt.event.ItemEvent;
23  import java.awt.event.ItemListener;
24  import java.beans.PropertyChangeEvent;
25  import java.beans.PropertyChangeListener;
26  import java.security.cert.Certificate;
27  
28  import javax.swing.AbstractAction;
29  import javax.swing.Action;
30  import javax.swing.BorderFactory;
31  import javax.swing.Box;
32  import javax.swing.JButton;
33  import javax.swing.JComboBox;
34  import javax.swing.JComponent;
35  import javax.swing.JEditorPane;
36  import javax.swing.JPanel;
37  import javax.swing.JPopupMenu;
38  import javax.swing.JScrollPane;
39  import javax.swing.JSeparator;
40  import javax.swing.JSplitPane;
41  import javax.swing.JTabbedPane;
42  import javax.swing.JTable;
43  import javax.swing.JToggleButton;
44  import javax.swing.JToolBar;
45  import javax.swing.SwingUtilities;
46  import javax.swing.event.ChangeEvent;
47  import javax.swing.event.ChangeListener;
48  import javax.swing.event.TableModelEvent;
49  import javax.swing.event.TableModelListener;
50  import javax.swing.text.html.HTMLEditorKit;
51  
52  import org.apache.log4j.Logger;
53  
54  import com.eviware.soapui.SoapUI;
55  import com.eviware.soapui.impl.wsdl.WsdlRequest;
56  import com.eviware.soapui.impl.wsdl.WsdlSubmitContext;
57  import com.eviware.soapui.impl.wsdl.actions.request.AddRequestToMockServiceAction;
58  import com.eviware.soapui.impl.wsdl.actions.request.CloneRequestAction;
59  import com.eviware.soapui.impl.wsdl.actions.request.CreateEmptyRequestAction;
60  import com.eviware.soapui.impl.wsdl.actions.request.RecreateRequestAction;
61  import com.eviware.soapui.impl.wsdl.actions.support.ShowOnlineHelpAction;
62  import com.eviware.soapui.impl.wsdl.panels.attachments.AttachmentPanel;
63  import com.eviware.soapui.impl.wsdl.panels.request.actions.WSIValidateRequestAction;
64  import com.eviware.soapui.impl.wsdl.panels.request.components.RequestMessageXmlEditor;
65  import com.eviware.soapui.impl.wsdl.panels.request.components.RequestXmlDocument;
66  import com.eviware.soapui.impl.wsdl.panels.request.components.ResponseMessageXmlEditor;
67  import com.eviware.soapui.impl.wsdl.panels.request.components.ResponseXmlDocument;
68  import com.eviware.soapui.impl.wsdl.panels.request.components.SoapMessageXmlEditor;
69  import com.eviware.soapui.impl.wsdl.panels.request.components.editor.XmlDocument;
70  import com.eviware.soapui.impl.wsdl.panels.request.components.editor.views.source.XmlSourceEditorView;
71  import com.eviware.soapui.impl.wsdl.panels.support.EndpointsComboBoxModel;
72  import com.eviware.soapui.impl.wsdl.submit.transports.http.SSLInfo;
73  import com.eviware.soapui.impl.wsdl.submit.transports.http.WsdlResponse;
74  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
75  import com.eviware.soapui.model.ModelItem;
76  import com.eviware.soapui.model.iface.Submit;
77  import com.eviware.soapui.model.iface.SubmitContext;
78  import com.eviware.soapui.model.iface.SubmitListener;
79  import com.eviware.soapui.model.iface.Request.SubmitException;
80  import com.eviware.soapui.model.iface.Submit.Status;
81  import com.eviware.soapui.settings.UISettings;
82  import com.eviware.soapui.support.UISupport;
83  import com.eviware.soapui.support.action.swing.SwingActionDelegate;
84  import com.eviware.soapui.support.actions.ChangeSplitPaneOrientationAction;
85  import com.eviware.soapui.support.components.JEditorStatusBarWithProgress;
86  import com.eviware.soapui.support.components.JXToolBar;
87  import com.eviware.soapui.support.swing.SoapUISplitPaneUI;
88  import com.eviware.soapui.support.types.StringToStringMap;
89  import com.eviware.soapui.support.xml.JXEditTextArea;
90  import com.eviware.soapui.ui.support.ModelItemDesktopPanel;
91  
92  /***
93   * Abstract DesktopPanel for WsdlRequests
94   * 
95   * @author Ole.Matzura
96   */
97  
98  public class AbstractWsdlRequestDesktopPanel<T extends ModelItem, T2 extends WsdlRequest> extends ModelItemDesktopPanel<T>
99  {
100 	private final static Logger log = Logger.getLogger(AbstractWsdlRequestDesktopPanel.class);
101 
102 	private JComboBox endpointCombo;
103 	private JButton submitButton;
104 	private JButton cancelButton;
105 	private EndpointsComboBoxModel endpointsModel;
106 	private JEditorStatusBarWithProgress statusBar;
107 	private JButton splitButton;
108 	private Submit submit;
109 	private JButton recreateButton;
110 	private JButton cloneButton;
111 	private JButton createEmptyButton;
112 	private InternalSubmitListener internalSubmitListener;
113 	private JSplitPane requestSplitPane;
114 	private MoveFocusAction moveFocusAction;
115 	private ClosePanelAction closePanelAction = new ClosePanelAction();
116 	private T2 request;
117 
118 	private SoapMessageXmlEditor requestEditor;
119 	private SoapMessageXmlEditor responseEditor;
120 
121 	//public WSIValidateRequestAction wsiValidateAction;
122 
123 	private JTabbedPane requestTabs;
124 	private JPanel requestTabPanel;
125 	private JToggleButton tabsButton;
126 
127 	public boolean responseHasFocus;
128 
129 	private JTabbedPane requestEditorTabs;
130 	private JTabbedPane responseEditorTabs;
131 
132 	private JPanel requestEditorTabPanel;
133 	private JPanel responseEditorTabPanel;
134 	private AttachmentPropertyChangeListener attachmentsPropertyChangeListener = new AttachmentPropertyChangeListener();
135 	private StringToStringMapTableModel requestHeadersModel;
136 	private JTable requestHeadersTable;
137 	private StringToStringMapTableModel responseHeadersModel;
138 
139 	private JEditorPane sslInfoPane;
140 	private JButton addToMockServiceButton;
141 	//private JButton addAsMockResponseStepButton;
142 	
143 //	private DefaultActionList formEditorActions;
144 
145 	private AbstractAction wsiValidateAction;
146 
147 	private AttachmentPanel requestAttachmentPanel;
148 	private AttachmentPanel responseAttachmentPanel;
149 
150 	public AbstractWsdlRequestDesktopPanel(T modelItem)
151 	{
152 		super(modelItem);
153 	}
154 
155 	protected void init(T2 request)
156 	{
157 		this.request = request;
158 		
159 		this.endpointsModel = new EndpointsComboBoxModel(request);
160 		internalSubmitListener = createSubmitListener();
161 
162 		request.addSubmitListener(internalSubmitListener);
163 
164 		add(buildContent(), BorderLayout.CENTER);
165 		add(buildToolbar(), BorderLayout.NORTH);
166 		add(buildStatusLabel(), BorderLayout.SOUTH);
167 
168 		setPreferredSize(new Dimension(600, 500));
169 		
170 		request.addPropertyChangeListener( attachmentsPropertyChangeListener );
171 		
172 		addFocusListener( new FocusAdapter() {
173 
174 			@Override
175 			public void focusGained( FocusEvent e )
176 			{
177 				if( requestTabs.getSelectedIndex() == 1 || responseHasFocus )
178 					responseEditor.requestFocusInWindow();
179 				else
180 					requestEditor.requestFocusInWindow();
181 			}} );
182 	}
183 
184 	protected InternalSubmitListener createSubmitListener()
185 	{
186 		return new InternalSubmitListener();
187 	}
188 	
189 	public final T2 getRequest()
190 	{
191 		return request;
192 	}
193 	
194 	public final SoapMessageXmlEditor getRequestEditor()
195 	{
196 		return requestEditor;
197 	}
198 
199 	public final SoapMessageXmlEditor getResponseEditor()
200 	{
201 		return responseEditor;
202 	}
203 	
204 	public Submit getSubmit()
205 	{
206 		return submit;
207 	}
208 
209 	protected JComponent buildStatusLabel()
210 	{
211 		statusBar = new JEditorStatusBarWithProgress();
212 		statusBar.setBorder( BorderFactory.createEmptyBorder( 1, 0, 0, 0 ));
213 		
214 		return statusBar;
215 	}
216 	
217 	public JEditorStatusBarWithProgress getStatusBar()
218 	{
219 		return statusBar;
220 	}
221 
222 	@SuppressWarnings("unchecked")
223 	protected JComponent buildContent()
224 	{
225 		requestSplitPane = UISupport.createHorizontalSplit();
226 		requestSplitPane.setResizeWeight(0.5);
227 		requestSplitPane.setBorder(null);
228 		
229 		submitButton = createActionButton(new SubmitAction(), true);
230 		cancelButton = createActionButton(new CancelAction(), false);
231 		splitButton = createActionButton(new ChangeSplitPaneOrientationAction(requestSplitPane), true);
232 		
233 		tabsButton = new JToggleButton( new ChangeToTabsAction() );
234 		tabsButton.setPreferredSize( UISupport.TOOLBAR_BUTTON_DIMENSION );
235 		
236 		recreateButton = createActionButton(new RecreateRequestAction(request), true);
237 		addToMockServiceButton = createActionButton( 
238 					SwingActionDelegate.createDelegate( AddRequestToMockServiceAction.SOAPUI_ACTION_ID, request, null, "/addToMockService.gif" ), true );
239 		
240 		cloneButton = createActionButton(
241 					SwingActionDelegate.createDelegate( CloneRequestAction.SOAPUI_ACTION_ID, request, null, "/clone_request.gif" ), true );
242 		
243 		createEmptyButton = createActionButton(new CreateEmptyRequestAction(request), true);
244 
245 		submitButton.setEnabled(request.getEndpoint() != null && request.getEndpoint().trim().length() > 0);
246 		wsiValidateAction = 
247 			SwingActionDelegate.createDelegate( new WSIValidateRequestAction(), request, "alt W" ); 
248 		wsiValidateAction.setEnabled( request.getResponse() != null );
249 		
250 		moveFocusAction = new MoveFocusAction();
251 
252 		requestEditor = buildRequestEditor();
253 		responseEditor = buildResponseEditor();
254 		
255 		requestEditorTabs = new JTabbedPane( JTabbedPane.BOTTOM );
256 		requestEditorTabs.setTabLayoutPolicy( JTabbedPane.SCROLL_TAB_LAYOUT );
257 		requestEditorTabs.addTab( "SOAP Request", requestEditor );
258 		requestAttachmentPanel = new AttachmentPanel( request, true );
259 		requestEditorTabs.addTab( "Request Attachments (" + request.getAttachmentCount() + ")"
260 				, requestAttachmentPanel);
261 		requestEditorTabs.addTab( "HTTP Headers (" + request.getRequestHeaders().size() + ")", createRequestHttpHeadersPanel() );
262 		requestEditorTabs.addChangeListener( new ChangeListener() {
263 
264 			public void stateChanged( ChangeEvent e )
265 			{
266 				SwingUtilities.invokeLater( new Runnable() {
267 
268 					public void run()
269 					{
270 						int ix = requestEditorTabs.getSelectedIndex();
271 						if( ix == 0 )
272 						  requestEditor.requestFocus(); 
273 					}}  );
274 				
275 			}} );
276 		
277 		responseEditorTabs = new JTabbedPane( JTabbedPane.BOTTOM );
278 		responseEditorTabs.setTabLayoutPolicy( JTabbedPane.SCROLL_TAB_LAYOUT );
279 		responseEditorTabs.addTab( "SOAP Response", responseEditor );
280 		responseAttachmentPanel = new AttachmentPanel( request, false );
281 		responseEditorTabs.addTab( "Response Attachments (" + (request.getResponse() == null ? 0 :
282 			request.getResponse().getAttachments().length) + ")", responseAttachmentPanel);
283 		responseEditorTabs.addTab( "HTTP Headers", createResponseHttpHeadersPanel() );
284 		String sslInfoTabTitle = "SSL Info";
285 		if( request.getResponse() != null && ((WsdlResponse)request.getResponse()).getSSLInfo() != null )
286 			sslInfoTabTitle += " (" + ((WsdlResponse)request.getResponse()).getSSLInfo().getPeerCertificates().length + " certs)";
287 		else
288 			sslInfoTabTitle += " (-)";
289 		
290 		responseEditorTabs.addTab( sslInfoTabTitle, createSSLInfoPanel() );
291 		responseEditorTabs.addChangeListener( new ChangeListener() {
292 
293 			public void stateChanged( ChangeEvent e )
294 			{
295 				SwingUtilities.invokeLater( new Runnable() {
296 
297 					public void run()
298 					{
299 						int ix = responseEditorTabs.getSelectedIndex();
300 						if( ix == 0 )
301 							responseEditor.requestFocus(); 
302 					}}  );
303 				
304 			}} );
305 		
306 		requestTabs = new JTabbedPane();
307 		requestTabs.addChangeListener( new ChangeListener() {
308 
309 			public void stateChanged( ChangeEvent e )
310 			{
311 				SwingUtilities.invokeLater( new Runnable() {
312 
313 					public void run()
314 					{
315 						int ix = requestTabs.getSelectedIndex();
316 						if( ix == 0 && requestEditorTabs.getSelectedIndex() == 0 )
317 							requestEditor.requestFocus(); 
318 						else if( ix == 1 && responseEditorTabs.getSelectedIndex() == 0 )
319 							responseEditor.requestFocus(); 
320 					}} );
321 			}} );
322 		
323 	   requestTabPanel = UISupport.createTabPanel( requestTabs, true );
324 	   
325 	   requestEditorTabPanel = UISupport.createTabPanel(requestEditorTabs, false);
326 		responseEditorTabPanel = UISupport.createTabPanel(responseEditorTabs, false);
327 
328 		if( request.getSettings().getBoolean( UISettings.START_WITH_REQUEST_TABS ))
329 	   {
330 	   	requestTabs.addTab( "Request", requestEditorTabPanel );
331 	   	requestTabs.addTab( "Response", responseEditorTabPanel );
332 	   	splitButton.setEnabled( false );
333 	   	tabsButton.setSelected( true );
334 	   	
335 	   	return requestTabPanel;
336 	   }
337 	   else
338 	   {
339 	   	requestSplitPane.setTopComponent(requestEditorTabPanel);
340 	   	requestSplitPane.setBottomComponent(responseEditorTabPanel);
341 	   	requestSplitPane.setDividerLocation(0.5);
342 	   	return requestSplitPane;
343 	   }
344 	}
345 
346 	private Component createSSLInfoPanel()
347 	{
348 		JPanel panel = new JPanel( new BorderLayout());
349 
350 		sslInfoPane = new JEditorPane();
351 		sslInfoPane.setEditorKit( new HTMLEditorKit() );
352 		panel.add( new JScrollPane( sslInfoPane ));
353 		
354 		return panel;
355 	}
356 
357 	private void updateSSLInfo( SSLInfo sslInfo )
358 	{
359 		String sslInfoTabTitle = "SSL Info";
360 		
361 		if( sslInfo == null )
362 		{
363 			sslInfoPane.setText( "<html><body>Not an SSL connection</body></html>" );
364 			sslInfoTabTitle += " (-)";
365 		}
366 		else
367 		{
368 			StringBuffer buf = new StringBuffer( "<html><body><table cellpadding=1 cellspacing=1 border=0>" );
369 			
370 			buf.append( "<tr><td><b>CipherSuite:</b></td><td align=left valign=top>" ).
371 				append( sslInfo.getCipherSuite().toString() ).append( "</td></tr>");
372 			
373 			if( sslInfo.getLocalPrincipal() != null )
374 				buf.append( "<tr><td><b>LocalPrincipal:</b></td><td align=left valign=top>" ).
375 					append( sslInfo.getLocalPrincipal().getName() ).append( "</td></tr>");
376 			
377 			Certificate[] localCertificates = sslInfo.getLocalCertificates();
378 			
379 			if( localCertificates != null)
380 			{
381 				buf.append( "</table><table cellpadding=0 cellspacing=0 border=0>" );
382 				
383 				int cnt = 1;
384 				for( Certificate cert : localCertificates )
385 				{
386 					buf.append( "<tr><td><b>Local Certificate " ).append( cnt++ ).
387 						append( ":</b><pre><font size=-1>" ).
388 						append( cert.toString() ).append( "</font></pre></td></tr>");
389 					
390 				}
391 				
392 				buf.append( "</table><table cellpadding=1 cellspacing=1 border=0>" );
393 			}
394 			
395 			if( sslInfo.getPeerPrincipal() != null )
396 				buf.append( "<tr><td><b>PeerPrincipal:</b></td><td align=left valign=top>" ).
397 					append( sslInfo.getPeerPrincipal().toString() ).append( "</td></tr>");
398 			
399 			Certificate[] peerCertificates = sslInfo.getPeerCertificates();
400 			if( peerCertificates != null)
401 			{
402 				buf.append( "</table><table cellpadding=0 cellspacing=0 border=0>" );
403 				
404 				int cnt = 1;
405 				for( Certificate cert : peerCertificates )
406 				{
407 					buf.append( "<tr><td colspan=2><b>Peer Certificate " ).append( cnt++ ).
408 						append( ":</b><pre><font size=-1>" ).
409 						append( cert.toString() ).append( "</font></pre></td></tr>");
410 				}
411 				
412 				buf.append( "</table><table cellpadding=0 cellspacing=0 border=0>" );
413 			}
414 			
415 			buf.append(  "</table></body></html>" );
416 			sslInfoPane.setText( buf.toString() );
417 			
418 			sslInfoTabTitle += " (" + sslInfo.getPeerCertificates().length + " certs)";
419 		}
420 		
421 		responseEditorTabs.setTitleAt( 3, sslInfoTabTitle );
422 	}
423 	
424 	private Component createResponseHttpHeadersPanel()
425 	{
426 		responseHeadersModel = new StringToStringMapTableModel( 
427 							request.getResponse() == null ? new StringToStringMap() : 
428 								request.getResponse().getResponseHeaders(), "Header", "Value", false );
429 		
430 		responseHeadersModel.addTableModelListener( new TableModelListener() 
431 		{
432 			public void tableChanged( TableModelEvent arg0 )
433 			{
434 				responseEditorTabs.setTitleAt( 2, "HTTP Headers (" + responseHeadersModel.getRowCount() + ")");
435 			}} );
436 		
437 		JTable responseHeadersTable = new JTable( responseHeadersModel );
438 		return new JScrollPane( responseHeadersTable );
439 	}
440 
441 	private Component createRequestHttpHeadersPanel()
442 	{
443 		requestHeadersModel = new StringToStringMapTableModel( request.getRequestHeaders(), "Header", "Value", true );
444 		requestHeadersModel.addTableModelListener( new TableModelListener() 
445 		{
446 			public void tableChanged( TableModelEvent arg0 )
447 			{
448 				getRequest().setRequestHeaders( requestHeadersModel.getData() );
449 				requestEditorTabs.setTitleAt( 2, "HTTP Headers (" + requestHeadersModel.getRowCount() + ")");
450 			}} );
451 		
452 		requestHeadersTable = new JTable( requestHeadersModel );
453 		JPanel panel = new JPanel( new BorderLayout() );
454 
455 		panel.add( new JScrollPane( requestHeadersTable ), BorderLayout.CENTER );
456 		
457 		JXToolBar builder = UISupport.createToolbar();
458 		builder.addFixed( new JButton( new AbstractAction( "Add" ) {
459 
460 			public void actionPerformed( ActionEvent arg0 )
461 			{
462 				Object header = UISupport.prompt( "Specify name of header to add", "Add HTTP Header", "" );
463 				if( header != null )
464 				{
465 					requestHeadersModel.add( header.toString(), "" );
466 				}
467 			}} ));
468 		builder.addRelatedGap();
469 		builder.addFixed( new JButton( new AbstractAction("Remove" ){
470 
471 			public void actionPerformed( ActionEvent arg0 )
472 			{
473 				int row = requestHeadersTable.getSelectedRow();
474 				if( row != -1 && UISupport.confirm( "Delete selected header?", "Remove Header" ) )
475 				{
476 					requestHeadersModel.remove( row );
477 				}
478 			}} ));
479 		
480 		
481 //		builder.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
482 		builder.addGlue();
483 		builder.addFixed( UISupport.createToolbarButton( new ShowOnlineHelpAction(HelpUrls.HEADERS_HELP_URL)));
484 
485 		panel.add( builder, BorderLayout.SOUTH );
486 		
487 		return panel;
488 	}
489 
490 	protected SoapMessageXmlEditor buildResponseEditor()
491 	{
492 		return new WsdlResponseMessageEditor( new ResponseXmlDocument( request ));
493 	}
494 
495 	protected SoapMessageXmlEditor buildRequestEditor()
496 	{
497 		return new WsdlRequestMessageEditor( new RequestXmlDocument( request ));
498 	}
499 
500 	protected JComponent buildToolbar()
501 	{
502 		endpointCombo = new JComboBox(endpointsModel);
503 		endpointCombo.setToolTipText(endpointsModel.getSelectedItem().toString());
504 
505 		endpointCombo.addItemListener(new ItemListener()
506 		{
507 			public void itemStateChanged(ItemEvent e)
508 			{
509 				Object item = endpointCombo.getSelectedItem();
510 				if (item == null)
511 				{
512 					endpointCombo.setToolTipText("- no endpoint set for request -");
513 				}
514 				else
515 				{
516 					String selectedItem = item.toString();
517 					endpointCombo.setToolTipText(selectedItem);
518 				}
519 
520 				submitButton.setEnabled(submit == null && request.getEndpoint() != null
521 						&& request.getEndpoint().trim().length() > 0);
522 			}
523 		});
524 
525 		JXToolBar toolbar =  UISupport.createToolbar(); 
526 		
527 		toolbar.add( submitButton );
528 		insertButtons( toolbar );
529 		
530 		toolbar.add( addToMockServiceButton);
531 //		toolbar.add( addAsMockResponseStepButton);
532 		
533 		toolbar.add(recreateButton);
534 		toolbar.add(createEmptyButton);
535 		toolbar.add(cloneButton);
536 		toolbar.add(cancelButton);
537 		toolbar.addSeparator();
538 		toolbar.add(endpointCombo);
539 		
540 		toolbar.add( Box.createHorizontalGlue() );
541 		toolbar.add( tabsButton );
542 		toolbar.add(splitButton);
543 		toolbar.add(UISupport.createToolbarButton(new ShowOnlineHelpAction(getHelpUrl())));
544 		
545 		return toolbar;
546 	}
547 
548 	protected String getHelpUrl()
549 	{
550 		return HelpUrls.REQUESTEDITOR_HELP_URL;
551 	}
552 
553 	protected void insertButtons(JToolBar toolbar)
554 	{
555 	}
556 
557 	public void setEnabled(boolean enabled)
558 	{
559 		endpointCombo.setEnabled(enabled);
560 		requestEditor.setEditable(enabled);
561 		responseEditor.setEditable(enabled);
562 
563 		submitButton.setEnabled(enabled && request.getEndpoint() != null
564 				&& request.getEndpoint().trim().length() > 0);
565 		recreateButton.setEnabled(enabled);
566 		createEmptyButton.setEnabled(enabled);
567 		cloneButton.setEnabled(enabled);
568 		
569 		statusBar.setIndeterminate( !enabled );
570 	}
571 
572 	private final class AttachmentPropertyChangeListener implements PropertyChangeListener
573 	{
574 		public void propertyChange(PropertyChangeEvent evt)
575 		{
576 			if( evt.getPropertyName().equals( WsdlRequest.ATTACHMENTS_PROPERTY ))
577 			{
578 				requestEditorTabs.setTitleAt( 1, "Request Attachments (" + request.getAttachmentCount() + ")");
579 			}
580 			else if( evt.getPropertyName().equals( WsdlRequest.RESPONSE_PROPERTY ))
581 			{
582 				responseEditorTabs.setTitleAt( 1, "Response Attachments (" + (request.getResponse() == null ? 0 :
583 					request.getResponse().getAttachments().length) + ")" );
584 			}
585 			else if( evt.getPropertyName().equals( WsdlRequest.REQUEST_HEADERS_PROPERTY ))
586 			{
587 				requestHeadersModel.setData( ( StringToStringMap ) evt.getNewValue() );
588 			}
589 		}
590 	}
591 	
592 	public class WsdlRequestMessageEditor extends RequestMessageXmlEditor<WsdlRequest>
593 	{
594 		private InputAreaFocusListener inputAreaFocusListener;
595 		private JXEditTextArea inputArea;
596 
597 		public WsdlRequestMessageEditor(XmlDocument document)
598 		{
599 			super(document, request );
600 			
601 			XmlSourceEditorView editor = getSourceEditor();
602 			inputArea = editor.getInputArea();
603 			inputArea.getInputHandler().addKeyBinding("A+ENTER", submitButton.getAction());
604 			inputArea.getInputHandler().addKeyBinding("A+X", cancelButton.getAction());
605 			inputArea.getInputHandler().addKeyBinding("AC+TAB", moveFocusAction);
606 			inputArea.getInputHandler().addKeyBinding("F5", recreateButton.getAction());
607 			inputArea.getInputHandler().addKeyBinding("C+F4", closePanelAction);
608 			
609 			inputAreaFocusListener = new InputAreaFocusListener();
610 			inputArea.addFocusListener( inputAreaFocusListener);
611 		}
612 
613 		@Override
614 		public void release()
615 		{
616 			super.release();
617 			inputArea.removeFocusListener( inputAreaFocusListener );
618 		}
619 	}
620 
621 	public class WsdlResponseMessageEditor extends ResponseMessageXmlEditor<WsdlRequest>
622 	{
623 		private JXEditTextArea inputArea;
624 		private ResultAreaFocusListener resultAreaFocusListener;
625 
626 		public WsdlResponseMessageEditor(XmlDocument document)
627 		{
628 			super(document, request );
629 		
630 			XmlSourceEditorView editor = getSourceEditor();
631 			
632 			inputArea = editor.getInputArea();
633 			resultAreaFocusListener = new ResultAreaFocusListener();
634 			inputArea.addFocusListener(resultAreaFocusListener);
635 			
636 			inputArea.getInputHandler().addKeyBinding("A+ENTER", submitButton.getAction());
637 			inputArea.getInputHandler().addKeyBinding("A+X", cancelButton.getAction());
638 			inputArea.getInputHandler().addKeyBinding("AC+TAB", moveFocusAction);
639 			inputArea.getInputHandler().addKeyBinding("C+F4", closePanelAction);
640 			
641 			JPopupMenu inputPopup = editor.getEditorPopup();
642 			inputPopup.insert( new JSeparator(), 2 );
643 			inputPopup.insert( wsiValidateAction, 3 );
644 		}
645 
646 		@Override
647 		public void release()
648 		{
649 			super.release();
650 			
651 			inputArea.removeFocusListener( resultAreaFocusListener );
652 		}
653 	}
654 	
655 	protected final class InputAreaFocusListener implements FocusListener
656 	{
657 		public void focusGained(FocusEvent e)
658 		{
659 			responseHasFocus = false;
660 			
661 			statusBar.setTarget( requestEditor.getSourceEditor().getInputArea() );
662 			if( !splitButton.isEnabled() )
663 			{
664 				requestTabs.setSelectedIndex( 0 );
665 				return;
666 			}
667 			
668 			if (getModelItem().getSettings().getBoolean(UISettings.NO_RESIZE_REQUEST_EDITOR))
669 				return;
670 
671 			// dont resize if split has been dragged
672 			if (((SoapUISplitPaneUI) requestSplitPane.getUI()).hasBeenDragged())
673 				return;
674 
675 			int pos = requestSplitPane.getDividerLocation();
676 			if (pos >= 600)
677 				return;
678 			if (requestSplitPane.getMaximumDividerLocation() > 700)
679 				requestSplitPane.setDividerLocation(600);
680 			else
681 				requestSplitPane.setDividerLocation(0.8);
682 		}
683 
684 		public void focusLost(FocusEvent e)
685 		{
686 		}
687 	}
688 
689 	protected final class ResultAreaFocusListener implements FocusListener
690 	{
691 		public void focusGained(FocusEvent e)
692 		{
693 			responseHasFocus = true;
694 			
695 			statusBar.setTarget( responseEditor.getSourceEditor().getInputArea() );
696 			if( !splitButton.isEnabled() )
697 			{
698 				requestTabs.setSelectedIndex( 1 );
699 				return;
700 			}
701 			
702 			if (request.getSettings().getBoolean(UISettings.NO_RESIZE_REQUEST_EDITOR))
703 				return;
704 
705 			// dont resize if split has been dragged or result is empty
706 			if (((SoapUISplitPaneUI) requestSplitPane.getUI()).hasBeenDragged() || request.getResponse() == null)
707 				return;
708 
709 			int pos = requestSplitPane.getDividerLocation();
710 			int maximumDividerLocation = requestSplitPane.getMaximumDividerLocation();
711 			if (pos + 600 < maximumDividerLocation)
712 				return;
713 
714 			if (maximumDividerLocation > 700)
715 				requestSplitPane.setDividerLocation(maximumDividerLocation - 600);
716 			else
717 				requestSplitPane.setDividerLocation(0.2);
718 		}
719 
720 		public void focusLost(FocusEvent e)
721 		{
722 		}
723 	}
724 
725 	public class SubmitAction extends AbstractAction
726 	{
727 		public SubmitAction()
728 		{
729 			putValue(Action.SMALL_ICON, UISupport.createImageIcon("/submit_request.gif"));
730 			putValue(Action.SHORT_DESCRIPTION, "Submit request to specified endpoint URL");
731 			putValue(Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "alt ENTER" ));
732 		}
733 
734 		public void actionPerformed(ActionEvent e)
735 		{
736 			if (submit != null && submit.getStatus() == Submit.Status.RUNNING)
737 			{
738 				if (UISupport.confirm("Cancel current request?", "Submit Request"))
739 				{
740 					submit.cancel();
741 				}
742 				else
743 					return;
744 			}
745 
746 			try
747 			{
748 				submit = doSubmit();
749 			}
750 			catch (SubmitException e1)
751 			{
752 				SoapUI.logError( e1 );
753 			}
754 		}
755 	}
756 	
757 	protected Submit doSubmit() throws SubmitException
758 	{
759 		return request.submit(new WsdlSubmitContext(null), true);
760 	}
761 
762 	private class CancelAction extends AbstractAction
763 	{
764 		public CancelAction()
765 		{
766 			super();
767 			putValue(Action.SMALL_ICON, UISupport.createImageIcon("/cancel_request.gif"));
768 			putValue(Action.SHORT_DESCRIPTION, "Aborts ongoing request");
769 			putValue(Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "alt X" ));
770 		}
771 
772 		public void actionPerformed(ActionEvent e)
773 		{
774 			if (submit == null)
775 				return;
776 
777 			cancelButton.setEnabled(false);
778 			submit.cancel();
779 			setEnabled(true);
780 			submit = null;
781 		}
782 	}
783 
784 	private class ClosePanelAction extends AbstractAction
785 	{
786 		public void actionPerformed(ActionEvent e)
787 		{
788 			SoapUI.getDesktop().closeDesktopPanel(getModelItem());
789 		}
790 	}
791 
792 	private class MoveFocusAction extends AbstractAction
793 	{
794 		public void actionPerformed(ActionEvent e)
795 		{
796 			if (requestEditor.hasFocus())
797 			{
798 				responseEditor.requestFocus();
799 			}
800 			else
801 			{
802 				requestEditor.requestFocus();
803 			}
804 		}
805 	}
806 
807 	protected class InternalSubmitListener implements SubmitListener
808 	{
809 		protected InternalSubmitListener()
810 		{
811 		}
812 
813 		public boolean beforeSubmit(Submit submit, SubmitContext context)
814 		{
815 			if (submit.getRequest() != request )
816 				return true;
817 			
818 			if( getModelItem().getSettings().getBoolean( UISettings.AUTO_VALIDATE_REQUEST ))
819 			{
820 				boolean result = requestEditor.saveDocument( true );
821 				if( !result &&  getModelItem().getSettings().getBoolean( UISettings.ABORT_ON_INVALID_REQUEST ))
822 				{
823 					statusBar.setInfo( "Cancelled request due to invalid content" );
824 					return false;
825 				}
826 			}
827 			else requestEditor.saveDocument( false );
828 
829 			setEnabled( false );
830 			cancelButton.setEnabled( AbstractWsdlRequestDesktopPanel.this.submit != null );
831 			wsiValidateAction.setEnabled( false );
832 			return true;
833 		}
834 
835 		public void afterSubmit(Submit submit, SubmitContext context)
836 		{
837 			if (submit.getRequest() != request )
838 				return;
839 			
840 			Status status = submit.getStatus();
841 			WsdlResponse response = (WsdlResponse) submit.getResponse();
842 			if (status != Status.CANCELED )
843 			{
844 				request.setResponse(response, context);
845 			}
846 			
847 			cancelButton.setEnabled(false);
848 			wsiValidateAction.setEnabled( request.getResponse() != null );
849 			setEnabled(true);
850 
851 			String message = null;
852 			String infoMessage = null;
853 			String requestName = request.getOperation().getInterface().getName() + "."
854 					+ request.getOperation().getName() + ":" + request.getName();
855 
856 			if (status == Status.CANCELED)
857 			{
858 				message = "CANCELED";
859 				infoMessage = "[" + requestName + "] - CANCELED";
860 			}
861 			else
862 			{
863 				if (status == Status.ERROR || response == null)
864 				{
865 					message = "Error getting response; " + submit.getError();
866 					infoMessage = "Error getting response for [" + requestName + "]; " + submit.getError();
867 				}
868 				else
869 				{
870 					message = "response time: " + response.getTimeTaken() + "ms (" + response.getContentLength() + " bytes)";
871 					infoMessage = "Got response for [" + requestName + "] in " + response.getTimeTaken() + "ms ("
872 							+ response.getContentLength() + " bytes)";
873 
874 					if( !splitButton.isEnabled() )
875 						requestTabs.setSelectedIndex( 1 );
876 					
877 					responseEditor.requestFocus();
878 				}
879 				
880 				responseHeadersModel.setData( submit.getResponse() == null ? null : submit.getResponse().getResponseHeaders());
881 				
882 				updateSSLInfo( response == null ? null : response.getSSLInfo() );
883 				wsiValidateAction.setEnabled( true );
884 			}
885 
886 			logMessages(message, infoMessage);
887 			
888 			if( getModelItem().getSettings().getBoolean( UISettings.AUTO_VALIDATE_RESPONSE ))
889 				responseEditor.getSourceEditor().validate();
890 
891 			AbstractWsdlRequestDesktopPanel.this.submit = null;
892 		}
893 
894 		protected void logMessages(String message, String infoMessage)
895 		{
896 			log.info(infoMessage);
897 			statusBar.setInfo(message);
898 		}
899 	}
900 
901 	public boolean onClose(boolean canCancel)
902 	{
903 		if (canCancel)
904 		{
905 			if (submit != null && submit.getStatus() == Submit.Status.RUNNING)
906 			{
907 				Boolean retVal = UISupport.confirmOrCancel("Cancel request before closing?", "Closing window");
908 				if (retVal == null)
909 					return false;
910 
911 				if (retVal.booleanValue() && submit.getStatus() == Submit.Status.RUNNING)
912 					submit.cancel();
913 			}
914 		}
915 		else if (submit != null && submit.getStatus() == Submit.Status.RUNNING)
916 		{
917 			submit.cancel();
918 		}
919 
920 		request.removeSubmitListener( internalSubmitListener );
921 		request.removePropertyChangeListener( attachmentsPropertyChangeListener );
922 		endpointsModel.release();
923 
924 		requestEditor.saveDocument( false );
925 		requestEditor.release();
926 		responseEditor.release();
927 		
928 		requestAttachmentPanel.release();
929 		responseAttachmentPanel.release();
930 		
931 		responseEditor.getParent().remove( responseEditor );
932 		responseEditorTabs.removeAll();
933 		requestEditor.getParent().remove( requestEditor );
934 		requestEditorTabs.removeAll();
935 		requestSplitPane.removeAll();
936 		sslInfoPane.getParent().remove( sslInfoPane );
937 		
938 		return super.release();
939 	}
940 
941 	public boolean dependsOn(ModelItem modelItem)
942 	{
943 		return modelItem == request || modelItem == request.getOperation()
944 				|| modelItem == request.getOperation().getInterface()
945 				|| modelItem == request.getOperation().getInterface().getProject();
946 	}
947 
948 	public String getDescription()
949 	{
950 		return "Request: [" + request.getName() + "] - " + request.getOperation().getInterface().getName() + 
951 		    "#" + request.getOperation().getName();
952 	}
953 	
954 	private final class ChangeToTabsAction extends AbstractAction
955 	{
956 		public ChangeToTabsAction()
957 		{
958 			putValue(Action.SMALL_ICON, UISupport.createImageIcon("/toggle_tabs.gif"));
959 			putValue(Action.SHORT_DESCRIPTION, "Toggles to tab-based layout");
960 		}
961 		
962 		public void actionPerformed(ActionEvent e)
963 		{
964 			if( splitButton.isEnabled() )
965 			{
966 				splitButton.setEnabled( false );
967 				removeContent( requestSplitPane );
968 				setContent( requestTabPanel );
969 				requestTabs.addTab( "Request", requestEditorTabPanel );
970 				requestTabs.addTab( "Response", responseEditorTabPanel );
971 				
972 				if( responseHasFocus )
973 				{
974 					requestTabs.setSelectedIndex( 1 );
975 					requestEditorTabs.requestFocus();
976 				}
977 				else
978 				{
979 					responseEditorTabs.requestFocus();
980 				}
981 			}
982 			else
983 			{
984 				int selectedIndex = requestTabs.getSelectedIndex();
985 				
986 				splitButton.setEnabled( true );
987 				removeContent( requestTabPanel );
988 				setContent( requestSplitPane );
989 				requestSplitPane.setTopComponent(requestEditorTabPanel);
990 		   	requestSplitPane.setBottomComponent(responseEditorTabPanel);
991 		   	requestSplitPane.setDividerLocation(0.5);
992 		   	
993 		   	if( selectedIndex == 0 )
994 		   		requestEditor.requestFocus();
995 		   	else
996 		   		responseEditor.requestFocus();
997 			}
998 			
999 			revalidate();
1000 		}
1001 	}
1002 	
1003 	public void setContent(JComponent content)
1004 	{
1005 		add( content, BorderLayout.CENTER );
1006 	}
1007 
1008 	public void removeContent(JComponent content)
1009 	{
1010 		remove( content );
1011 	}
1012 }