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