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