1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.impl.support.panels;
14
15 import com.eviware.soapui.SoapUI;
16 import com.eviware.soapui.impl.support.AbstractHttpRequest;
17 import com.eviware.soapui.impl.support.EndpointsComboBoxModel;
18 import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction;
19 import com.eviware.soapui.impl.support.components.ModelItemXmlEditor;
20 import com.eviware.soapui.impl.support.components.RequestMessageXmlEditor;
21 import com.eviware.soapui.impl.support.components.ResponseMessageXmlEditor;
22 import com.eviware.soapui.impl.wsdl.submit.transports.http.HttpResponse;
23 import com.eviware.soapui.model.ModelItem;
24 import com.eviware.soapui.model.iface.Request.SubmitException;
25 import com.eviware.soapui.model.iface.Submit;
26 import com.eviware.soapui.model.iface.Submit.Status;
27 import com.eviware.soapui.model.iface.SubmitContext;
28 import com.eviware.soapui.model.iface.SubmitListener;
29 import com.eviware.soapui.settings.UISettings;
30 import com.eviware.soapui.support.UISupport;
31 import com.eviware.soapui.support.actions.ChangeSplitPaneOrientationAction;
32 import com.eviware.soapui.support.components.JEditorStatusBarWithProgress;
33 import com.eviware.soapui.support.components.JXToolBar;
34 import com.eviware.soapui.support.editor.views.xml.source.XmlSourceEditorView;
35 import com.eviware.soapui.support.editor.xml.XmlDocument;
36 import com.eviware.soapui.support.swing.SoapUISplitPaneUI;
37 import com.eviware.soapui.support.xml.JXEditTextArea;
38 import com.eviware.soapui.ui.support.ModelItemDesktopPanel;
39 import org.apache.log4j.Logger;
40
41 import javax.swing.*;
42 import javax.swing.event.ChangeEvent;
43 import javax.swing.event.ChangeListener;
44 import java.awt.*;
45 import java.awt.event.*;
46 import java.beans.PropertyChangeEvent;
47
48 /***
49 * Abstract DesktopPanel for HttpRequests
50 *
51 * @author Ole.Matzura
52 */
53
54 public abstract class AbstractHttpRequestDesktopPanel<T extends ModelItem, T2 extends AbstractHttpRequest<?>> extends
55 ModelItemDesktopPanel<T> implements SubmitListener
56 {
57 private final static Logger log = Logger.getLogger( AbstractHttpRequestDesktopPanel.class );
58
59 private JComponent endpointComponent;
60 private JButton submitButton;
61 private JButton cancelButton;
62 private EndpointsComboBoxModel endpointsModel;
63 private JEditorStatusBarWithProgress statusBar;
64 private JButton splitButton;
65 private Submit submit;
66 private JSplitPane requestSplitPane;
67 private MoveFocusAction moveFocusAction;
68 private ClosePanelAction closePanelAction = new ClosePanelAction();
69 private T2 request;
70
71 private ModelItemXmlEditor<?, ?> requestEditor;
72 private ModelItemXmlEditor<?, ?> responseEditor;
73
74 private JTabbedPane requestTabs;
75 private JPanel requestTabPanel;
76 private JToggleButton tabsButton;
77
78 private boolean responseHasFocus;
79 private SubmitAction submitAction;
80 private boolean hasClosed;
81
82 public AbstractHttpRequestDesktopPanel( T modelItem, T2 request )
83 {
84 super( modelItem );
85
86 this.request = request;
87
88 init( request );
89
90 try
91 {
92
93 if( request.getAttachmentCount() > 0 )
94 {
95 request.getOperation().getInterface().getDefinitionContext().loadIfNecessary();
96 }
97 }
98 catch( Exception e )
99 {
100 e.printStackTrace();
101 }
102 }
103
104 protected void init( T2 request )
105 {
106 this.endpointsModel = new EndpointsComboBoxModel( request );
107
108 request.addSubmitListener( this );
109 request.addPropertyChangeListener( this );
110
111 add( buildContent(), BorderLayout.CENTER );
112 add( buildToolbar(), BorderLayout.NORTH );
113 add( buildStatusLabel(), BorderLayout.SOUTH );
114
115 setPreferredSize( new Dimension( 600, 500 ) );
116
117 addFocusListener( new FocusAdapter()
118 {
119
120 @Override
121 public void focusGained( FocusEvent e )
122 {
123 if( requestTabs.getSelectedIndex() == 1 || responseHasFocus )
124 responseEditor.requestFocusInWindow();
125 else
126 requestEditor.requestFocusInWindow();
127 }
128 } );
129 }
130
131 public final T2 getRequest()
132 {
133 return request;
134 }
135
136 public final ModelItemXmlEditor<?, ?> getRequestEditor()
137 {
138 return requestEditor;
139 }
140
141 public final ModelItemXmlEditor<?, ?> getResponseEditor()
142 {
143 return responseEditor;
144 }
145
146 public Submit getSubmit()
147 {
148 return submit;
149 }
150
151 protected JComponent buildStatusLabel()
152 {
153 statusBar = new JEditorStatusBarWithProgress();
154 statusBar.setBorder( BorderFactory.createEmptyBorder( 1, 0, 0, 0 ) );
155
156 return statusBar;
157 }
158
159 public JEditorStatusBarWithProgress getStatusBar()
160 {
161 return statusBar;
162 }
163
164 protected JComponent buildContent()
165 {
166 requestSplitPane = UISupport.createHorizontalSplit();
167 requestSplitPane.setResizeWeight( 0.5 );
168 requestSplitPane.setBorder( null );
169
170 submitAction = new SubmitAction();
171 submitButton = createActionButton( submitAction, true );
172 submitButton.setEnabled( request.getEndpoint() != null && request.getEndpoint().trim().length() > 0 );
173
174 cancelButton = createActionButton( new CancelAction(), false );
175 splitButton = createActionButton( new ChangeSplitPaneOrientationAction( requestSplitPane ), true );
176
177 tabsButton = new JToggleButton( new ChangeToTabsAction() );
178 tabsButton.setPreferredSize( UISupport.TOOLBAR_BUTTON_DIMENSION );
179
180 moveFocusAction = new MoveFocusAction();
181
182 requestEditor = buildRequestEditor();
183 responseEditor = buildResponseEditor();
184
185 requestTabs = new JTabbedPane();
186 requestTabs.addChangeListener( new ChangeListener()
187 {
188
189 public void stateChanged( ChangeEvent e )
190 {
191 SwingUtilities.invokeLater( new Runnable()
192 {
193
194 public void run()
195 {
196 int ix = requestTabs.getSelectedIndex();
197 if( ix == 0 )
198 requestEditor.requestFocus();
199 else if( ix == 1 && responseEditor != null )
200 responseEditor.requestFocus();
201 }
202 } );
203 }
204 } );
205
206 requestTabPanel = UISupport.createTabPanel( requestTabs, true );
207
208 if( request.getSettings().getBoolean( UISettings.START_WITH_REQUEST_TABS ) )
209 {
210 requestTabs.addTab( "Request", requestEditor );
211 if( responseEditor != null )
212 requestTabs.addTab( "Response", responseEditor );
213 splitButton.setEnabled( false );
214 tabsButton.setSelected( true );
215
216 return requestTabPanel;
217 }
218 else
219 {
220 requestSplitPane.setTopComponent( requestEditor );
221 requestSplitPane.setBottomComponent( responseEditor );
222 requestSplitPane.setDividerLocation( 0.5 );
223 return requestSplitPane;
224 }
225 }
226
227 public SubmitAction getSubmitAction()
228 {
229 return submitAction;
230 }
231
232 protected abstract ModelItemXmlEditor<?, ?> buildResponseEditor();
233
234 protected abstract ModelItemXmlEditor<?, ?> buildRequestEditor();
235
236 protected JComponent buildToolbar()
237 {
238 endpointComponent = buildEndpointComponent();
239
240 JXToolBar toolbar = UISupport.createToolbar();
241 toolbar.add( submitButton );
242
243 insertButtons( toolbar );
244
245 toolbar.add( cancelButton );
246
247 if( endpointComponent != null )
248 {
249 toolbar.addSeparator();
250 toolbar.add( endpointComponent );
251 }
252
253 toolbar.add( Box.createHorizontalGlue() );
254 toolbar.add( tabsButton );
255 toolbar.add( splitButton );
256 toolbar.add( UISupport.createToolbarButton( new ShowOnlineHelpAction( getHelpUrl() ) ) );
257
258 return toolbar;
259 }
260
261 protected JComponent buildEndpointComponent()
262 {
263 final JComboBox endpointCombo = new JComboBox( endpointsModel );
264 endpointCombo.setToolTipText( endpointsModel.getSelectedItem().toString() );
265
266 endpointCombo.addItemListener( new ItemListener()
267 {
268 public void itemStateChanged( ItemEvent e )
269 {
270 Object item = endpointCombo.getSelectedItem();
271 if( item == null )
272 {
273 endpointCombo.setToolTipText( "- no endpoint set for request -" );
274 }
275 else
276 {
277 String selectedItem = item.toString();
278 endpointCombo.setToolTipText( selectedItem );
279 }
280 }
281 } );
282
283 return endpointCombo;
284 }
285
286 public void propertyChange( PropertyChangeEvent evt )
287 {
288 if( evt.getPropertyName().equals( AbstractHttpRequest.ENDPOINT_PROPERTY ) )
289 {
290 submitButton.setEnabled( submit == null && request.getEndpoint() != null
291 && request.getEndpoint().trim().length() > 0 );
292 }
293
294 super.propertyChange( evt );
295 }
296
297 public JButton getSubmitButton()
298 {
299 return submitButton;
300 }
301
302 protected abstract String getHelpUrl();
303
304 protected abstract void insertButtons( JXToolBar toolbar );
305
306 public void setEnabled( boolean enabled )
307 {
308 if( endpointComponent != null )
309 endpointComponent.setEnabled( enabled );
310
311 requestEditor.setEditable( enabled );
312 if( responseEditor != null )
313 responseEditor.setEditable( enabled );
314
315 submitButton.setEnabled( enabled && request.hasEndpoint() );
316
317 statusBar.setIndeterminate( !enabled );
318 }
319
320 public abstract class AbstractHttpRequestMessageEditor<T3 extends XmlDocument> extends RequestMessageXmlEditor<T2, T3>
321 {
322 private InputAreaFocusListener inputAreaFocusListener;
323 private JXEditTextArea inputArea;
324
325 public AbstractHttpRequestMessageEditor( T3 document )
326 {
327 super( document, request );
328
329 XmlSourceEditorView editor = getSourceEditor();
330 if( editor != null )
331 {
332 inputArea = editor.getInputArea();
333 inputArea.getInputHandler().addKeyBinding( "A+ENTER", submitButton.getAction() );
334 inputArea.getInputHandler().addKeyBinding( "A+X", cancelButton.getAction() );
335 inputArea.getInputHandler().addKeyBinding( "AC+TAB", moveFocusAction );
336 inputArea.getInputHandler().addKeyBinding( "C+F4", closePanelAction );
337
338 inputAreaFocusListener = new InputAreaFocusListener( editor );
339 inputArea.addFocusListener( inputAreaFocusListener );
340 }
341 }
342
343 @Override
344 public void release()
345 {
346 super.release();
347 if( inputArea != null )
348 inputArea.removeFocusListener( inputAreaFocusListener );
349 }
350 }
351
352 public abstract class AbstractHttpResponseMessageEditor<T3 extends XmlDocument> extends ResponseMessageXmlEditor<T2, T3>
353 {
354 private JXEditTextArea inputArea;
355 private ResultAreaFocusListener resultAreaFocusListener;
356
357 public AbstractHttpResponseMessageEditor( T3 document )
358 {
359 super( document, request );
360
361 XmlSourceEditorView editor = getSourceEditor();
362
363 inputArea = editor.getInputArea();
364 if( inputArea != null )
365 {
366 resultAreaFocusListener = new ResultAreaFocusListener( editor );
367 inputArea.addFocusListener( resultAreaFocusListener );
368
369 inputArea.getInputHandler().addKeyBinding( "A+ENTER", submitButton.getAction() );
370 inputArea.getInputHandler().addKeyBinding( "A+X", cancelButton.getAction() );
371 inputArea.getInputHandler().addKeyBinding( "AC+TAB", moveFocusAction );
372 inputArea.getInputHandler().addKeyBinding( "C+F4", closePanelAction );
373 }
374 }
375
376 @Override
377 public void release()
378 {
379 super.release();
380
381 if( inputArea != null )
382 inputArea.removeFocusListener( resultAreaFocusListener );
383 }
384 }
385
386 protected final class InputAreaFocusListener implements FocusListener
387 {
388 private final XmlSourceEditorView sourceEditor;
389
390 public InputAreaFocusListener( XmlSourceEditorView editor )
391 {
392 this.sourceEditor = editor;
393 }
394
395 public void focusGained( FocusEvent e )
396 {
397 responseHasFocus = false;
398
399 statusBar.setTarget( sourceEditor.getInputArea() );
400 if( !splitButton.isEnabled() )
401 {
402 requestTabs.setSelectedIndex( 0 );
403 return;
404 }
405
406 if( getModelItem().getSettings().getBoolean( UISettings.NO_RESIZE_REQUEST_EDITOR ) )
407 return;
408
409
410 if( ( (SoapUISplitPaneUI) requestSplitPane.getUI() ).hasBeenDragged() )
411 return;
412
413 int pos = requestSplitPane.getDividerLocation();
414 if( pos >= 600 )
415 return;
416 if( requestSplitPane.getMaximumDividerLocation() > 700 )
417 requestSplitPane.setDividerLocation( 600 );
418 else
419 requestSplitPane.setDividerLocation( 0.8 );
420 }
421
422 public void focusLost( FocusEvent e )
423 {
424 }
425 }
426
427 protected final class ResultAreaFocusListener implements FocusListener
428 {
429 private final XmlSourceEditorView sourceEditor;
430
431 public ResultAreaFocusListener( XmlSourceEditorView editor )
432 {
433 this.sourceEditor = editor;
434 }
435
436 public void focusGained( FocusEvent e )
437 {
438 responseHasFocus = true;
439
440 statusBar.setTarget( sourceEditor.getInputArea() );
441 if( !splitButton.isEnabled() )
442 {
443 requestTabs.setSelectedIndex( 1 );
444 return;
445 }
446
447 if( request.getSettings().getBoolean( UISettings.NO_RESIZE_REQUEST_EDITOR ) )
448 return;
449
450
451 if( ( (SoapUISplitPaneUI) requestSplitPane.getUI() ).hasBeenDragged() || request.getResponse() == null )
452 return;
453
454 int pos = requestSplitPane.getDividerLocation();
455 int maximumDividerLocation = requestSplitPane.getMaximumDividerLocation();
456 if( pos + 600 < maximumDividerLocation )
457 return;
458
459 if( maximumDividerLocation > 700 )
460 requestSplitPane.setDividerLocation( maximumDividerLocation - 600 );
461 else
462 requestSplitPane.setDividerLocation( 0.2 );
463 }
464
465 public void focusLost( FocusEvent e )
466 {
467 }
468 }
469
470 public class SubmitAction extends AbstractAction
471 {
472 public SubmitAction()
473 {
474 putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/submit_request.gif" ) );
475 putValue( Action.SHORT_DESCRIPTION, "Submit request to specified endpoint URL" );
476 putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "alt ENTER" ) );
477 }
478
479 public void actionPerformed( ActionEvent e )
480 {
481 onSubmit();
482 }
483 }
484
485 protected abstract Submit doSubmit() throws SubmitException;
486
487 private class CancelAction extends AbstractAction
488 {
489 public CancelAction()
490 {
491 super();
492 putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/cancel_request.gif" ) );
493 putValue( Action.SHORT_DESCRIPTION, "Aborts ongoing request" );
494 putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "alt X" ) );
495 }
496
497 public void actionPerformed( ActionEvent e )
498 {
499 onCancel();
500 }
501 }
502
503 private class ClosePanelAction extends AbstractAction
504 {
505 public void actionPerformed( ActionEvent e )
506 {
507 SoapUI.getDesktop().closeDesktopPanel( getModelItem() );
508 }
509 }
510
511 private class MoveFocusAction extends AbstractAction
512 {
513 public void actionPerformed( ActionEvent e )
514 {
515 if( requestEditor.hasFocus() )
516 {
517 responseEditor.requestFocus();
518 }
519 else
520 {
521 requestEditor.requestFocus();
522 }
523 }
524 }
525
526 public boolean beforeSubmit( Submit submit, SubmitContext context )
527 {
528 if( submit.getRequest() != request )
529 return true;
530
531 if( getModelItem().getSettings().getBoolean( UISettings.AUTO_VALIDATE_REQUEST ) )
532 {
533 boolean result = requestEditor.saveDocument( true );
534 if( !result && getModelItem().getSettings().getBoolean( UISettings.ABORT_ON_INVALID_REQUEST ) )
535 {
536 statusBar.setInfo( "Cancelled request due to invalid content" );
537 return false;
538 }
539 }
540 else
541 {
542 requestEditor.saveDocument( false );
543 }
544
545 setEnabled( false );
546 cancelButton.setEnabled( AbstractHttpRequestDesktopPanel.this.submit != null );
547 return true;
548 }
549
550 public void afterSubmit( Submit submit, SubmitContext context )
551 {
552 if( submit.getRequest() != request )
553 return;
554
555 Status status = submit.getStatus();
556 HttpResponse response = (HttpResponse) submit.getResponse();
557 if( status != Status.CANCELED )
558 {
559 request.setResponse( response, context );
560 }
561
562 if( hasClosed )
563 {
564 request.removeSubmitListener(this);
565 return;
566 }
567
568 cancelButton.setEnabled( false );
569 setEnabled( true );
570
571 String message = null;
572 String infoMessage = null;
573 String requestName = request.getOperation() == null ? request.getName() :
574 request.getOperation().getInterface().getName() + "." + request.getOperation().getName()
575 + ":" + request.getName();
576
577 if( status == Status.CANCELED )
578 {
579 message = "CANCELED";
580 infoMessage = "[" + requestName + "] - CANCELED";
581 }
582 else
583 {
584 if( status == Status.ERROR || response == null )
585 {
586 message = "Error getting response; " + submit.getError();
587 infoMessage = "Error getting response for [" + requestName + "]; " + submit.getError();
588 }
589 else
590 {
591 message = "response time: " + response.getTimeTaken() + "ms (" + response.getContentLength() + " bytes)";
592 infoMessage = "Got response for [" + requestName + "] in " + response.getTimeTaken() + "ms ("
593 + response.getContentLength() + " bytes)";
594
595 if( !splitButton.isEnabled() )
596 requestTabs.setSelectedIndex( 1 );
597
598 responseEditor.requestFocus();
599 }
600 }
601
602 logMessages( message, infoMessage );
603
604 if( getModelItem().getSettings().getBoolean( UISettings.AUTO_VALIDATE_RESPONSE ) )
605 responseEditor.getSourceEditor().validate();
606
607 AbstractHttpRequestDesktopPanel.this.submit = null;
608 }
609
610 protected void logMessages( String message, String infoMessage )
611 {
612 log.info( infoMessage );
613 statusBar.setInfo( message );
614 }
615
616 public boolean onClose( boolean canCancel )
617 {
618 if( canCancel )
619 {
620 if( submit != null && submit.getStatus() == Submit.Status.RUNNING )
621 {
622 Boolean retVal = UISupport.confirmOrCancel( "Cancel request before closing?", "Closing window" );
623 if( retVal == null )
624 return false;
625
626 if( retVal.booleanValue() && submit.getStatus() == Submit.Status.RUNNING )
627 {
628 submit.cancel();
629 }
630
631 hasClosed = true;
632 }
633 else
634 {
635 request.removeSubmitListener( this );
636 }
637 }
638 else if( submit != null && submit.getStatus() == Submit.Status.RUNNING )
639 {
640 submit.cancel();
641 hasClosed = true;
642 }
643 else
644 {
645 request.removeSubmitListener( this );
646 }
647
648 request.removePropertyChangeListener( this );
649 requestEditor.saveDocument( false );
650
651 if( responseEditor != null )
652 responseEditor.getParent().remove( responseEditor );
653
654 requestEditor.getParent().remove( requestEditor );
655 requestSplitPane.removeAll();
656
657 return release();
658 }
659
660 @Override
661 protected boolean release()
662 {
663 endpointsModel.release();
664 requestEditor.release();
665
666 if( responseEditor != null )
667 responseEditor.release();
668
669 return super.release();
670 }
671
672 public boolean dependsOn( ModelItem modelItem )
673 {
674 return request.dependsOn( modelItem );
675 }
676
677 private final class ChangeToTabsAction extends AbstractAction
678 {
679 public ChangeToTabsAction()
680 {
681 putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/toggle_tabs.gif" ) );
682 putValue( Action.SHORT_DESCRIPTION, "Toggles to tab-based layout" );
683 }
684
685 public void actionPerformed( ActionEvent e )
686 {
687 if( splitButton.isEnabled() )
688 {
689 splitButton.setEnabled( false );
690 removeContent( requestSplitPane );
691 setContent( requestTabPanel );
692 requestTabs.addTab( "Request", requestEditor );
693
694 if( responseEditor != null )
695 requestTabs.addTab( "Response", responseEditor );
696
697 if( responseHasFocus )
698 {
699 requestTabs.setSelectedIndex( 1 );
700 requestEditor.requestFocus();
701 }
702 }
703 else
704 {
705 int selectedIndex = requestTabs.getSelectedIndex();
706
707 splitButton.setEnabled( true );
708 removeContent( requestTabPanel );
709 setContent( requestSplitPane );
710 requestSplitPane.setTopComponent( requestEditor );
711 if( responseEditor != null )
712 requestSplitPane.setBottomComponent( responseEditor );
713 requestSplitPane.setDividerLocation( 0.5 );
714
715 if( selectedIndex == 0 || responseEditor == null )
716 requestEditor.requestFocus();
717 else
718 responseEditor.requestFocus();
719 }
720
721 revalidate();
722 }
723 }
724
725 public void setContent( JComponent content )
726 {
727 add( content, BorderLayout.CENTER );
728 }
729
730 public void removeContent( JComponent content )
731 {
732 remove( content );
733 }
734
735 protected void onSubmit()
736 {
737 if( submit != null && submit.getStatus() == Submit.Status.RUNNING )
738 {
739 if( UISupport.confirm( "Cancel current request?", "Submit Request" ) )
740 {
741 submit.cancel();
742 }
743 else
744 return;
745 }
746
747 try
748 {
749 submit = doSubmit();
750 }
751 catch( SubmitException e1 )
752 {
753 SoapUI.logError( e1 );
754 }
755 }
756
757 protected void onCancel()
758 {
759 if( submit == null )
760 return;
761
762 cancelButton.setEnabled( false );
763 submit.cancel();
764 setEnabled( true );
765 submit = null;
766 }
767
768 public boolean isHasClosed()
769 {
770 return hasClosed;
771 }
772 }