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