View Javadoc

1   /*
2    *  soapui, copyright (C) 2005 Ole Matzura / eviware.com 
3    *
4    *  SoapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of the GNU Lesser General Public License as published by the Free Software Foundation; 
6    *  either version 2.1 of the License, or (at your option) any later version.
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.wsdl.panels.request;
14  
15  import java.awt.BorderLayout;
16  import java.awt.Component;
17  import java.awt.Dimension;
18  import java.awt.Toolkit;
19  import java.awt.event.ActionEvent;
20  import java.awt.event.FocusEvent;
21  import java.awt.event.FocusListener;
22  import java.awt.event.ItemEvent;
23  import java.awt.event.ItemListener;
24  import java.awt.event.KeyEvent;
25  import java.awt.event.MouseAdapter;
26  import java.awt.event.MouseEvent;
27  import java.beans.PropertyChangeEvent;
28  import java.beans.PropertyChangeListener;
29  
30  import javax.swing.AbstractAction;
31  import javax.swing.Action;
32  import javax.swing.BorderFactory;
33  import javax.swing.DefaultListModel;
34  import javax.swing.JButton;
35  import javax.swing.JComboBox;
36  import javax.swing.JComponent;
37  import javax.swing.JLabel;
38  import javax.swing.JList;
39  import javax.swing.JOptionPane;
40  import javax.swing.JPanel;
41  import javax.swing.JPopupMenu;
42  import javax.swing.JScrollPane;
43  import javax.swing.JSplitPane;
44  import javax.swing.KeyStroke;
45  import javax.swing.event.DocumentEvent;
46  import javax.swing.event.DocumentListener;
47  
48  import org.apache.commons.logging.Log;
49  import org.apache.commons.logging.LogFactory;
50  
51  import com.eviware.soapui.SoapUI;
52  import com.eviware.soapui.impl.wsdl.WsdlRequest;
53  import com.eviware.soapui.impl.wsdl.actions.request.AddToTestCaseAction;
54  import com.eviware.soapui.impl.wsdl.actions.request.CloneRequestAction;
55  import com.eviware.soapui.impl.wsdl.actions.request.CreateEmptyRequestAction;
56  import com.eviware.soapui.impl.wsdl.actions.request.RecreateRequestAction;
57  import com.eviware.soapui.impl.wsdl.panels.request.actions.ValidateRequestXmlAction;
58  import com.eviware.soapui.impl.wsdl.panels.request.actions.ValidateResponseXmlAction;
59  import com.eviware.soapui.impl.wsdl.panels.request.support.EndpointsComboBoxModel;
60  import com.eviware.soapui.impl.wsdl.teststeps.assertions.AssertionError;
61  import com.eviware.soapui.model.DesktopPanel;
62  import com.eviware.soapui.model.ModelItem;
63  import com.eviware.soapui.model.iface.Submit;
64  import com.eviware.soapui.model.iface.SubmitListener;
65  import com.eviware.soapui.model.iface.Submit.Status;
66  import com.eviware.soapui.support.JXmlTextArea;
67  import com.eviware.soapui.support.SoapUISplitPaneUI;
68  import com.eviware.soapui.support.UISupport;
69  import com.eviware.soapui.support.actions.ChangeSplitPaneOrientationAction;
70  import com.eviware.soapui.support.actions.FormatXmlAction;
71  import com.eviware.soapui.support.actions.SaveXmlTextAreaAction;
72  import com.jgoodies.forms.builder.ButtonBarBuilder;
73  
74  /***
75   * DesktopPanel for WsdlRequests
76   * 
77   * @author Ole.Matzura
78   */
79  
80  public class WsdlRequestDesktopPanel extends JPanel implements DesktopPanel
81  {
82     private final static Log log = LogFactory.getLog( WsdlRequestDesktopPanel.class );
83  
84     private JXmlTextArea resultArea;
85     private JXmlTextArea inputArea;
86     private JComboBox endpointCombo;
87     private JButton submitButton;
88     private JButton cancelButton;
89     private JComboBox encodingCombo;
90     private WsdlRequest request;
91     private EndpointsComboBoxModel endpointsModel;
92     private JLabel statusLabel;
93     private InternalPropertyChangeListener internalPropertyChangeListener;
94     private JButton splitButton;
95  	private Submit submit;
96  	private JButton recreateButton;
97  	private JButton cloneButton;
98  	private JButton createEmptyButton;
99  	private JButton addToTestCaseButton;
100 	private InternalSubmitListener internalSubmitListener;
101 	private JSplitPane requestSplitPane;
102 	private MoveFocusAction moveFocusAction;
103 
104    public WsdlRequestDesktopPanel( WsdlRequest request )
105    {
106       super( new BorderLayout() );
107       this.request = (WsdlRequest) request;
108       this.endpointsModel = new EndpointsComboBoxModel( request );
109       
110       internalSubmitListener = new InternalSubmitListener();
111       internalPropertyChangeListener = new InternalPropertyChangeListener();
112 
113       request.addSubmitListener( internalSubmitListener );
114       request.addPropertyChangeListener( internalPropertyChangeListener );
115       request.getOperation().getInterface().addPropertyChangeListener( internalPropertyChangeListener ); 
116       
117       add( buildUI(), BorderLayout.CENTER );
118       add( buildSearchBar(), BorderLayout.NORTH );
119       statusLabel = new JLabel("- no response -");
120       add( statusLabel, BorderLayout.SOUTH );
121       
122       setPreferredSize( new Dimension( 600, 500 ));
123    }
124 
125    private JComponent buildUI()
126    {
127    	requestSplitPane = UISupport.createHorizontalSplit();
128 		submitButton = createActionButton( new SubmitAction(), true );
129       cancelButton = createActionButton( new CancelAction(), false );
130       splitButton = createActionButton( new ChangeSplitPaneOrientationAction( requestSplitPane ), true );
131       recreateButton = createActionButton( new RecreateRequestAction(request), true );
132       cloneButton = createActionButton( new CloneRequestAction( request ), true );
133       createEmptyButton = createActionButton( new CreateEmptyRequestAction(request), true );
134       addToTestCaseButton = createActionButton( new AddToTestCaseAction( request ), true );
135       
136       submitButton.setEnabled( request.getEndpoint() != null && request.getEndpoint().trim().length() > 0 );
137 
138       moveFocusAction = new MoveFocusAction();
139       
140       requestSplitPane.setTopComponent( buildInputUI() );
141       requestSplitPane.setBottomComponent( buildOutputUI() );
142       requestSplitPane.setDividerLocation( 0.5 );
143       requestSplitPane.setResizeWeight( 0.5 );
144       requestSplitPane.setBorder( null );
145       
146       return requestSplitPane;
147    }
148    
149    private JButton createActionButton( Action action, boolean enabled )
150    {
151    	JButton button = new JButton( action );
152    	action.putValue( Action.NAME, null );
153    	button.setPreferredSize( new Dimension(24, 24 ));
154    	button.setEnabled( enabled );
155    	return button;
156    }
157 
158    private Component buildOutputUI()
159    {
160       resultArea = new JXmlTextArea();
161       resultArea.setMinimumSize( new Dimension( 50, 50 ));
162       resultArea.setToolTipText( "Response Area" );
163       if( request.getResponseContent() != null )
164       {
165    		resultArea.setText( request.getResponseContent() );
166    		resultArea.setCaretPosition( 0 );
167       }
168       resultArea.addFocusListener( new ResultAreaFocusListener() );
169       
170       DefaultListModel listModel = new DefaultListModel();
171       JList list = new JList( listModel );
172 		list.setToolTipText( "Response Validation Errors" );
173       list.addMouseListener( new ValidationListMouseAdapter( list, resultArea ));
174       
175 		JScrollPane scrollPane = new JScrollPane( list );
176 		scrollPane.setVisible( false );
177 		
178 		JSplitPane resultAreaSplit = UISupport.createVerticalSplit();
179       JPopupMenu popup = new JPopupMenu();
180       
181       popup = new JPopupMenu();
182       ValidateResponseXmlAction validateResponseXmlAction = new ValidateResponseXmlAction( request, scrollPane, listModel, resultAreaSplit, resultArea );
183       popup.add( validateResponseXmlAction );
184       FormatXmlAction formatXmlAction = new FormatXmlAction(resultArea);
185 		popup.add( formatXmlAction);
186 	   popup.addSeparator();
187       popup.add( resultArea.getUndoAction() );
188       popup.add( resultArea.getRedoAction() );
189       popup.add( resultArea.getCopyAction() );
190       popup.add( resultArea.getCutAction() );
191       popup.add( resultArea.getPasteAction() );      
192       popup.addSeparator();
193       popup.add( resultArea.getFindAndReplaceAction() );
194       popup.addSeparator();
195       SaveXmlTextAreaAction saveXmlTextAreaAction = new SaveXmlTextAreaAction(resultArea, "Save Response");
196 		popup.add( saveXmlTextAreaAction);
197       
198       resultArea.setRightClickPopup( popup );
199       
200       resultArea.getInputHandler().addKeyBinding( "A+ENTER", submitButton.getAction() );
201       resultArea.getInputHandler().addKeyBinding( "A+X", cancelButton.getAction() );
202       resultArea.getInputHandler().addKeyBinding( "A+V", validateResponseXmlAction );
203       resultArea.getInputHandler().addKeyBinding( "A+F", formatXmlAction );
204       resultArea.getInputHandler().addKeyBinding( "A+S", saveXmlTextAreaAction );
205       resultArea.getInputHandler().addKeyBinding( "S+TAB", moveFocusAction );
206      
207       resultAreaSplit.setTopComponent( resultArea );
208 		resultAreaSplit.setBottomComponent( scrollPane);
209       resultAreaSplit.setDividerLocation( 1.0 );
210       
211       return resultAreaSplit;
212    }
213 
214    private Component buildInputUI()
215    {
216       inputArea = new JXmlTextArea();
217       inputArea.setMinimumSize( new Dimension( 50, 50 ));
218       inputArea.setToolTipText( "Request Area" );
219       inputArea.setText( request.getRequestContent() );
220       inputArea.setCaretPosition( 0 );
221       inputArea.setDiscardEditsOnSet( false );
222       inputArea.addFocusListener( new InputAreaFocusListener());
223       
224       DefaultListModel listModel = new DefaultListModel();
225       JList list = new JList( listModel );
226       list.setToolTipText( "Request Validation Errors" );
227       list.addMouseListener( new ValidationListMouseAdapter( list, inputArea ));
228 		JScrollPane scrollPane = new JScrollPane( list );
229 		scrollPane.setVisible( false );
230 		
231 		JSplitPane inputAreaSplit = UISupport.createVerticalSplit();
232       
233       JPopupMenu inputPopup = new JPopupMenu();
234       ValidateRequestXmlAction validateRequestXmlAction = new ValidateRequestXmlAction( request, scrollPane, listModel, inputAreaSplit, inputArea );
235 		inputPopup.add( validateRequestXmlAction);
236       FormatXmlAction formatXmlAction = new FormatXmlAction(inputArea);
237 		inputPopup.add( formatXmlAction);
238       inputPopup.addSeparator();
239       inputPopup.add( inputArea.getUndoAction() );
240       inputPopup.add( inputArea.getRedoAction() );
241       inputPopup.add( inputArea.getCopyAction() );
242       inputPopup.add( inputArea.getCutAction() );
243       inputPopup.add( inputArea.getPasteAction() );
244       inputPopup.addSeparator();
245       inputPopup.add( inputArea.getFindAndReplaceAction() );
246       inputPopup.addSeparator();
247       SaveXmlTextAreaAction saveXmlTextAreaAction = new SaveXmlTextAreaAction(inputArea, "Save Request");
248 		inputPopup.add( saveXmlTextAreaAction);
249       inputArea.setRightClickPopup( inputPopup );
250       
251       inputArea.getDocument().addDocumentListener( new DocumentListener() {
252 
253          public void insertUpdate(DocumentEvent e)
254          {
255             request.setRequest( inputArea.getText() );
256          }
257 
258          public void removeUpdate(DocumentEvent e)
259          {
260             request.setRequest( inputArea.getText() );
261          }
262 
263          public void changedUpdate(DocumentEvent e)
264          {
265          }} );
266       
267       inputArea.getInputHandler().addKeyBinding( "A+ENTER", submitButton.getAction() );
268       inputArea.getInputHandler().addKeyBinding( "A+X", cancelButton.getAction() );
269       inputArea.getInputHandler().addKeyBinding( "A+V", validateRequestXmlAction );
270       inputArea.getInputHandler().addKeyBinding( "A+F", formatXmlAction );
271       inputArea.getInputHandler().addKeyBinding( "A+S", saveXmlTextAreaAction );
272       inputArea.getInputHandler().addKeyBinding( "S+TAB", moveFocusAction );
273      
274       inputAreaSplit.setTopComponent( inputArea );
275 		inputAreaSplit.setBottomComponent( scrollPane);
276       inputAreaSplit.setDividerLocation( 1.0 );
277       
278       return inputAreaSplit;
279    }
280 
281 	private JPanel buildSearchBar()
282    {
283       endpointCombo = new JComboBox( endpointsModel);
284       endpointCombo.setToolTipText( endpointsModel.getSelectedItem().toString()  );
285       endpointCombo.setPreferredSize( new Dimension( 300, 20 ));
286       
287       endpointCombo.addItemListener( new ItemListener() {
288 
289          public void itemStateChanged(ItemEvent e)
290          {
291             Object item = endpointCombo.getSelectedItem();
292             if( item == null )
293             {
294             	endpointCombo.setToolTipText( "- no endpoint set for request -" );
295             }
296             else
297             {
298 					String selectedItem = item.toString();
299 	            endpointCombo.setToolTipText( selectedItem );
300             }
301             
302             submitButton.setEnabled( submit == null && request.getEndpoint() != null && request.getEndpoint().trim().length() > 0 );
303          }});
304       
305       encodingCombo = new JComboBox();
306       encodingCombo.setEditable( true );
307       encodingCombo.addItem( "UTF-8" );
308       encodingCombo.addItem( "iso-8859-1" );
309       encodingCombo.setSelectedItem( request.getEncoding() );
310       encodingCombo.setToolTipText( "Sets the encoding to use for the request" );
311       
312       encodingCombo.addItemListener( new ItemListener() {
313 
314          public void itemStateChanged(ItemEvent e)
315          {
316             request.setEncoding( encodingCombo.getSelectedItem().toString() );
317          }});
318       
319       ButtonBarBuilder builder = new ButtonBarBuilder();
320      
321       builder.addFixed( submitButton );
322       builder.addRelatedGap();
323       builder.addFixed( addToTestCaseButton );
324       builder.addRelatedGap();
325       builder.addFixed( recreateButton );
326       builder.addRelatedGap();
327       builder.addFixed( createEmptyButton );
328       builder.addRelatedGap();
329       builder.addFixed( cloneButton );
330       builder.addRelatedGap();
331       builder.addFixed( cancelButton );
332       builder.addRelatedGap();
333       builder.addFixed( endpointCombo );
334       builder.addRelatedGap();
335       builder.addFixed( encodingCombo );
336       builder.addRelatedGap();
337       builder.addFixed( splitButton );
338       builder.setBorder( BorderFactory.createEmptyBorder( 3, 3, 3, 3 ) );
339       return builder.getPanel();
340    }
341 
342    public void setEnabled( boolean enabled )
343    {
344       endpointCombo.setEnabled( enabled );
345       inputArea.setEditable( enabled );
346       resultArea.setEditable( enabled );
347       submitButton.setEnabled( enabled && request.getEndpoint() != null && request.getEndpoint().trim().length() > 0 );
348       addToTestCaseButton.setEnabled( enabled );
349       recreateButton.setEnabled( enabled );
350       createEmptyButton.setEnabled( enabled );
351       cloneButton.setEnabled( enabled );
352       encodingCombo.setEnabled( enabled );
353    }
354    
355    private final class ValidationListMouseAdapter extends MouseAdapter
356 	{
357 		private final JList list;
358 		private final JXmlTextArea textArea;
359 
360 		public ValidationListMouseAdapter(JList list, JXmlTextArea textArea)
361 		{
362 			this.list = list;
363 			this.textArea = textArea;
364 		}
365 
366 		public void mouseClicked(MouseEvent e)
367 		{
368 		   if( e.getClickCount() < 2 ) return;
369 		
370 		   int ix = list.getSelectedIndex();
371 		   if( ix == -1 ) return;
372 		   
373 		   Object obj = list.getModel().getElementAt( ix );
374 		   if( obj instanceof AssertionError )
375 		   {
376 		   	AssertionError error = (AssertionError) obj;
377 		   	if( error.getLineNumber() >= 0 )
378 		   	{
379 		   		textArea.setCaretPosition( textArea.getLineStartOffset( error.getLineNumber()-1 ) );
380 		   		textArea.requestFocus();
381 		   	}
382 		   	else Toolkit.getDefaultToolkit().beep();
383 		   }
384 		   else Toolkit.getDefaultToolkit().beep();
385 		}
386 	}
387 
388 	private final class InputAreaFocusListener implements FocusListener
389 	{
390 		public void focusGained(FocusEvent e)
391 		{
392 			// dont resize if split has been dragged 
393 			if( ((SoapUISplitPaneUI)requestSplitPane.getUI()).hasBeenDragged() ) return;
394 			
395 			int pos = requestSplitPane.getDividerLocation();
396 			if( pos >= 600 ) return;
397 			if( requestSplitPane.getMaximumDividerLocation() > 700 )
398 				requestSplitPane.setDividerLocation( 600 );
399 			else
400 			   requestSplitPane.setDividerLocation( 0.8 );
401 		}
402 
403 		public void focusLost(FocusEvent e)
404 		{
405 		}
406 	}
407    
408    private final class ResultAreaFocusListener implements FocusListener
409 	{
410 		public void focusGained(FocusEvent e)
411 		{
412 			// dont resize if split has been dragged or result is empty
413 			if( ((SoapUISplitPaneUI)requestSplitPane.getUI()).hasBeenDragged() || resultArea.getText().trim().length() == 0 ) 
414 				return;
415 			
416 			int pos = requestSplitPane.getDividerLocation();
417 			int maximumDividerLocation = requestSplitPane.getMaximumDividerLocation();
418 			if( pos + 600 < maximumDividerLocation ) return;
419 			
420 			if( maximumDividerLocation > 700 )
421 				requestSplitPane.setDividerLocation( maximumDividerLocation-600 );
422 			else
423 				requestSplitPane.setDividerLocation( 0.2 );
424 		}
425 
426 		public void focusLost(FocusEvent e)
427 		{
428 		}
429 	}
430 
431 	public class SubmitAction extends AbstractAction
432    {
433       public SubmitAction()
434       {
435          putValue( Action.SMALL_ICON, SoapUI.createImageIcon( "/submit_request.gif"));
436          putValue( Action.SHORT_DESCRIPTION, "Submit request to specified endpoint URL" );
437          putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, KeyEvent.ALT_MASK ));
438       }
439 
440       public void actionPerformed( ActionEvent e )
441       {
442       	if( submit != null && submit.getStatus() == Submit.Status.RUNNING )
443       	{
444       		if( UISupport.confirm( "Cancel current request?", "Submit Request" ))
445       		{
446       			submit.cancel();
447       		}
448       		else return;
449       	}
450       	
451          submit = request.submit();
452       }
453    }
454 
455    private class CancelAction extends AbstractAction
456    {
457       public CancelAction()
458       {
459          super();
460          putValue( Action.SMALL_ICON, SoapUI.createImageIcon( "/cancel_request.gif"));
461          putValue( Action.SHORT_DESCRIPTION, "Aborts ongoing request" );
462          putValue( Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke( KeyEvent.VK_X, KeyEvent.ALT_MASK ));
463       }
464 
465       public void actionPerformed( ActionEvent e )
466       {
467       	if( submit == null ) return;
468       	
469          cancelButton.setEnabled( false );
470          WsdlRequestDesktopPanel.this.setEnabled( true );
471          submit.cancel();
472       }
473    }
474 
475    private class MoveFocusAction extends AbstractAction
476    {
477 		public void actionPerformed(ActionEvent e)
478 		{
479 			if( inputArea.hasFocus() )
480 				resultArea.requestFocus();
481 			else 
482 			   inputArea.requestFocus();
483 		}
484    }
485    
486    private class InternalSubmitListener implements SubmitListener
487    {
488       private InternalSubmitListener()
489       {
490       }
491 
492       public boolean beforeSubmit(Submit submit)
493       {
494       	if( submit.getRequest() != request ) return true;
495       	
496          setEnabled( false );
497          cancelButton.setEnabled( true );
498          return true;
499       }
500 
501       public void afterSubmit(Submit submit)
502       {
503       	if( submit.getRequest() != request ) return;
504 
505       	Status status = submit.getStatus();
506          if( status != Status.CANCELED )
507          {
508             cancelButton.setEnabled( false );
509             setEnabled( true );
510             
511             request.setResponse( submit.getResponse( false ).getContentAsString() );
512          }
513          
514          String message = null;
515          String infoMessage = null;
516          String requestName = request.getOperation().getInterface().getName() + "." + 
517             request.getOperation().getName() + ":" + request.getName();
518 
519          if( status == Status.CANCELED ) 
520          {
521             message = "CANCELED";
522             infoMessage = "[" + requestName + "] - CANCELED";
523          }
524          else
525          {
526             if( status == Status.ERROR )
527             {
528                message = "Error getting response; " + submit.getError().getMessage();
529                infoMessage = "Error getting response for [" + requestName + "]; " + submit.getError().getMessage();
530             }
531             else
532             {
533                message = "response time: " + submit.getTimeTaken() + "ms (" + 
534                   submit.getResponse( false ).getContentLength() + " bytes)";   
535                infoMessage = "Got response for [" + requestName + "] in " + submit.getTimeTaken() + "ms (" + 
536                   submit.getResponse( false ).getContentLength() + " bytes)";   
537                
538                resultArea.requestFocus();
539             }
540          }
541          
542          log.info( infoMessage );
543          statusLabel.setText( message  );
544          
545          WsdlRequestDesktopPanel.this.submit = null;
546       }
547    }
548    
549    private class InternalPropertyChangeListener implements PropertyChangeListener
550    {
551       public void propertyChange(PropertyChangeEvent evt)
552       {
553          String propertyName = evt.getPropertyName();
554          if( propertyName.equals( WsdlRequest.RESPONSE_PROPERTY ))
555          {
556             resultArea.setText( request.getResponseContent() );
557             resultArea.setCaretPosition( 0 );
558          }
559          else if( propertyName.equals( WsdlRequest.ENCODING_PROPERTY ))
560          {
561             encodingCombo.setSelectedItem( request.getEncoding() );
562          }
563          else if( propertyName.equals( WsdlRequest.REQUEST_PROPERTY ))
564          {
565             String requestContent = request.getRequestContent();
566             if( !requestContent.equals( inputArea.getText()))
567             	inputArea.setText( requestContent );
568          }
569       }
570    }
571 
572 	public ModelItem getModelItem()
573 	{
574 		return request;
575 	}
576 
577 	public boolean onClose()
578 	{
579 		if( submit != null && submit.getStatus() == Submit.Status.RUNNING )
580 		{
581 			int retVal = JOptionPane.showConfirmDialog( SoapUI.getInstance().getFrame(), 
582 					"Cancel request before closing?", "Closing window", JOptionPane.YES_NO_CANCEL_OPTION );
583 			if( retVal == JOptionPane.CANCEL_OPTION ) return false;
584 			
585 			if( retVal == JOptionPane.YES_OPTION && submit.getStatus() == Submit.Status.RUNNING )
586 				submit.cancel();
587 		}
588 		
589       request.removeSubmitListener( internalSubmitListener );
590       request.removePropertyChangeListener( internalPropertyChangeListener );
591       request.getOperation().getInterface().removePropertyChangeListener( internalPropertyChangeListener ); 
592 
593       endpointsModel.release();
594       
595       return true;
596 	}
597 
598 	public JComponent getComponent()
599 	{
600 		return this;
601 	}
602 }