View Javadoc

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