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        try
91        {
92           // required to avoid deadlock in UI when opening attachments inspector
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          // dont resize if split has been dragged
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          // dont resize if split has been dragged or result is empty
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 }