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