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.wsdl.mock.dispatch;
14  
15  import com.eviware.soapui.SoapUI;
16  import com.eviware.soapui.config.MockOperationQueryMatchDispatchConfig;
17  import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction;
18  import com.eviware.soapui.impl.wsdl.mock.*;
19  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
20  import com.eviware.soapui.model.propertyexpansion.PropertyExpansionUtils;
21  import com.eviware.soapui.support.AbstractPropertyChangeNotifier;
22  import com.eviware.soapui.support.StringUtils;
23  import com.eviware.soapui.support.UISupport;
24  import com.eviware.soapui.support.components.JXToolBar;
25  import com.eviware.soapui.support.components.SimpleBindingForm;
26  import com.eviware.soapui.support.xml.XmlUtils;
27  import com.eviware.soapui.ui.support.ModelItemDesktopPanel;
28  import com.jgoodies.binding.PresentationModel;
29  import org.apache.xmlbeans.XmlCursor;
30  import org.apache.xmlbeans.XmlException;
31  import org.apache.xmlbeans.XmlObject;
32  
33  import javax.swing.*;
34  import javax.swing.event.ListSelectionEvent;
35  import javax.swing.event.ListSelectionListener;
36  import java.awt.*;
37  import java.awt.event.ActionEvent;
38  import java.beans.PropertyChangeEvent;
39  import java.beans.PropertyChangeListener;
40  import java.util.ArrayList;
41  import java.util.HashMap;
42  import java.util.List;
43  import java.util.Map;
44  
45  public class QueryMatchMockOperationDispatcher extends AbstractMockOperationDispatcher implements PropertyChangeListener
46  {
47     private MockOperationQueryMatchDispatchConfig conf;
48     private List<Query> queries = new ArrayList<Query>();
49     private PresentationModel<Query> queryDetailFormPresentationModel;
50     private QueryItemListModel queryItemListModel;
51     private JList itemList;
52     private JButton deleteButton;
53     private JButton copyButton;
54     private JButton renameButton;
55     private SimpleBindingForm detailForm;
56     private JButton runButton;
57     private JButton declareNsButton = new JButton( new DeclareNamespacesAction() );
58     private JButton extractFromCurrentButton = new JButton( new ExtractFromCurrentAction() );
59  
60     public QueryMatchMockOperationDispatcher( WsdlMockOperation mockOperation )
61     {
62        super( mockOperation );
63  
64        try
65        {
66           conf = MockOperationQueryMatchDispatchConfig.Factory.parse( getConfig().xmlText() );
67  
68           for( MockOperationQueryMatchDispatchConfig.Query query : conf.getQueryList() )
69           {
70              queries.add( new Query( query ) );
71           }
72        }
73        catch( XmlException e )
74        {
75           e.printStackTrace();
76        }
77  
78        mockOperation.addPropertyChangeListener( "mockResponses", this );
79     }
80  
81     @Override
82     public void release()
83     {
84        getMockOperation().removePropertyChangeListener( "mockResponses", this );
85        super.release();
86     }
87  
88     @Override
89     public JComponent buildEditorComponent()
90     {
91        JSplitPane splitPane = UISupport.createHorizontalSplit( buildQueryListComponent(), buildQueryDetailComponent() );
92        splitPane.setDividerLocation( 300 );
93        setEnabled();
94        return splitPane;
95     }
96  
97     protected Component buildQueryListComponent()
98     {
99        JPanel panel = new JPanel( new BorderLayout() );
100 
101       queryItemListModel = new QueryItemListModel();
102       itemList = new JList( queryItemListModel );
103       itemList.setCellRenderer( new QueryItemListCellRenderer() );
104       itemList.addListSelectionListener( new ListSelectionListener()
105       {
106          public void valueChanged( ListSelectionEvent e )
107          {
108             queryDetailFormPresentationModel.setBean( (Query) itemList.getSelectedValue() );
109             setEnabled();
110          }
111       } );
112 
113       panel.add( buildItemsToolbar(), BorderLayout.NORTH );
114       panel.add( new JScrollPane( itemList ), BorderLayout.CENTER );
115 
116       return panel;
117    }
118 
119    protected void setEnabled()
120    {
121       QueryMatchMockOperationDispatcher.Query bean = queryDetailFormPresentationModel.getBean();
122 
123       detailForm.setEnabled( bean != null );
124       renameButton.setEnabled( bean != null );
125       deleteButton.setEnabled( bean != null );
126       copyButton.setEnabled( bean != null );
127       extractFromCurrentButton.setEnabled( bean != null );
128       copyButton.setEnabled( bean != null );
129       declareNsButton.setEnabled( bean != null );
130       runButton.setEnabled( getQueryCount() > 0 );
131    }
132 
133    private JXToolBar buildItemsToolbar()
134    {
135       JXToolBar toolbar = UISupport.createSmallToolbar();
136 
137       runButton = UISupport.createToolbarButton( new RunAction() );
138       toolbar.addFixed( runButton );
139       toolbar.addSeparator();
140 
141       toolbar.addFixed( UISupport.createToolbarButton( new AddAction() ) );
142       deleteButton = UISupport.createToolbarButton( new DeleteAction() );
143       deleteButton.setEnabled( false );
144       toolbar.addFixed( deleteButton );
145       toolbar.addSeparator();
146       copyButton = UISupport.createToolbarButton( new CopyAction() );
147       copyButton.setEnabled( false );
148       toolbar.addFixed( copyButton );
149       renameButton = UISupport.createToolbarButton( new RenameAction() );
150       renameButton.setEnabled( false );
151       toolbar.addFixed( renameButton );
152 
153       toolbar.addSeparator();
154 
155       return toolbar;
156    }
157 
158    protected Component buildQueryDetailComponent()
159    {
160       queryDetailFormPresentationModel = new PresentationModel<Query>( null );
161       detailForm = new SimpleBindingForm( queryDetailFormPresentationModel );
162 
163       detailForm.setDefaultTextAreaRows( 8 );
164       detailForm.setDefaultTextAreaColumns( 50 );
165 
166       detailForm.append( buildQueryToolbar() );
167       detailForm.appendTextArea( "query", "XPath", "The XPath to query in the request" );
168       detailForm.appendTextArea( "match", "Expected Value", "The value to match" );
169       JComboBox comboBox = detailForm.appendComboBox( "response", "Dispatch to", new MockResponsesComboBoxModel(), "The MockResponse to dispatch to" );
170       UISupport.setFixedSize( comboBox, 150, 20 );
171       detailForm.appendCheckBox( "disabled", "Disabled", "Disables this Query" );
172 
173       return new JScrollPane( detailForm.getPanel() );
174    }
175 
176    protected JXToolBar buildQueryToolbar()
177    {
178       JXToolBar toolBar = UISupport.createSmallToolbar();
179 
180       addlQueryToolbarActions( toolBar );
181 
182       toolBar.addGlue();
183       toolBar.addFixed( ModelItemDesktopPanel.createActionButton(
184               new ShowOnlineHelpAction( HelpUrls.MOCKOPERATION_QUERYMATCHDISPATCH_HELP_URL ), true ) );
185 
186       return toolBar;
187    }
188 
189    protected void addlQueryToolbarActions( JXToolBar toolBar )
190    {
191       toolBar.addFixed( declareNsButton );
192       toolBar.addFixed( extractFromCurrentButton );
193    }
194 
195    public WsdlMockResponse selectMockResponse( WsdlMockRequest request, WsdlMockResult result ) throws DispatchException
196    {
197       Map<String, XmlCursor> cursorCache = new HashMap<String, XmlCursor>();
198 
199       try
200       {
201          XmlObject xmlObject = request.getRequestXmlObject();
202 
203          for( Query query : getQueries() )
204          {
205             if( query.isDisabled() )
206                continue;
207 
208             String path = PropertyExpansionUtils.expandProperties( request.getContext(), query.getQuery() );
209             if( StringUtils.hasContent( path ) )
210             {
211                XmlCursor cursor = cursorCache.get( path );
212                if( cursor == null && !cursorCache.containsKey( path ) )
213                {
214                   cursor = xmlObject.newCursor();
215                   cursor.selectPath( path );
216                   if( !cursor.toNextSelection() )
217                   {
218                      cursor.dispose();
219                      cursor = null;
220                   }
221                }
222 
223                if( cursor != null )
224                {
225                   String value = PropertyExpansionUtils.expandProperties( request.getContext(), query.getMatch() );
226 
227                   if( value.equals( XmlUtils.getValueForMatch( cursor ) ) )
228                   {
229                      return getMockOperation().getMockResponseByName( query.getResponse() );
230                   }
231                }
232 
233                cursorCache.put( path, cursor );
234             }
235          }
236 
237          return null;
238       }
239       catch( Throwable e )
240       {
241          throw new DispatchException( e );
242       }
243       finally
244       {
245          for( XmlCursor cursor : cursorCache.values() )
246          {
247             if( cursor != null )
248             {
249                cursor.dispose();
250             }
251          }
252       }
253    }
254 
255    public Query addQuery( String name )
256    {
257       Query query = new Query( conf.addNewQuery() );
258       query.setName( name );
259       queries.add( query );
260 
261       getPropertyChangeSupport().firePropertyChange( "queries", null, query );
262 
263       if( queryItemListModel != null )
264          queryItemListModel.fireAdded();
265 
266       return query;
267    }
268 
269    public void deleteQuery( Query query )
270    {
271       int ix = queries.indexOf( query );
272       queries.remove( ix );
273       getPropertyChangeSupport().firePropertyChange( "queries", query, null );
274 
275       if( queryItemListModel != null )
276          queryItemListModel.fireRemoved( ix );
277 
278       conf.removeQuery( ix );
279       saveConfig();
280    }
281 
282    public Query[] getQueries()
283    {
284       return queries.toArray( new Query[queries.size()] );
285    }
286 
287    public int getQueryCount()
288    {
289       return queries.size();
290    }
291 
292    public Query getQueryAt( int index )
293    {
294       return queries.get( index );
295    }
296 
297    public void propertyChange( PropertyChangeEvent evt )
298    {
299       if( queryItemListModel != null )
300          queryItemListModel.refresh();
301    }
302 
303    public static class Factory implements MockOperationDispatchFactory
304    {
305       public MockOperationDispatcher build( WsdlMockOperation mockOperation )
306       {
307          return new QueryMatchMockOperationDispatcher( mockOperation );
308       }
309    }
310 
311    public class Query extends AbstractPropertyChangeNotifier
312    {
313       private MockOperationQueryMatchDispatchConfig.Query config;
314 
315       protected Query( MockOperationQueryMatchDispatchConfig.Query config )
316       {
317          this.config = config;
318       }
319 
320       public String getName()
321       {
322          return config.getName();
323       }
324 
325       public void setName( String s )
326       {
327          String old = config.getName();
328          config.setName( s );
329          saveConfig();
330          firePropertyChange( "name", old, s );
331       }
332 
333       public boolean isDisabled()
334       {
335          return config.getDisabled();
336       }
337 
338       public void setDisabled( boolean disabled )
339       {
340          boolean old = config.getDisabled();
341          if( old == disabled )
342             return;
343          config.setDisabled( disabled );
344          saveConfig();
345          firePropertyChange( "disabled", old, disabled );
346          queryItemListModel.refresh();
347       }
348 
349       public String getQuery()
350       {
351          return config.getQuery();
352       }
353 
354       public void setQuery( String s )
355       {
356          String old = config.getQuery();
357          config.setQuery( s );
358          saveConfig();
359          firePropertyChange( "query", old, s );
360       }
361 
362       public String getMatch()
363       {
364          return config.getMatch();
365       }
366 
367       public void setMatch( String s )
368       {
369          String old = config.getMatch();
370          config.setMatch( s );
371          saveConfig();
372          firePropertyChange( "match", old, s );
373       }
374 
375       public String getResponse()
376       {
377          return config.getResponse();
378       }
379 
380       public void setResponse( String s )
381       {
382          String old = config.getResponse();
383          config.setResponse( s );
384          saveConfig();
385          firePropertyChange( "response", old, s );
386       }
387    }
388 
389    private void saveConfig()
390    {
391       saveConfig( conf );
392    }
393 
394    private class QueryItemListModel extends AbstractListModel
395    {
396       public int getSize()
397       {
398          return getQueryCount();
399       }
400 
401       public Object getElementAt( int index )
402       {
403          return getQueryAt( index );
404       }
405 
406       public void refresh()
407       {
408          fireContentsChanged( this, 0, getQueryCount() );
409       }
410 
411       public void fireAdded()
412       {
413          fireIntervalAdded( this, getQueryCount(), getQueryCount() );
414       }
415 
416       public void fireRemoved( int index )
417       {
418          fireIntervalRemoved( this, index, index );
419       }
420    }
421 
422    private class MockResponsesComboBoxModel extends AbstractListModel implements ComboBoxModel
423    {
424       public int getSize()
425       {
426          return getMockOperation().getMockResponseCount();
427       }
428 
429       public Object getElementAt( int index )
430       {
431          return getMockOperation().getMockResponseAt( index ).getName();
432       }
433 
434       public void setSelectedItem( Object anItem )
435       {
436          Query query = getSelectedQuery();
437          if( query != null )
438             query.setResponse( String.valueOf( anItem ) );
439       }
440 
441       public Object getSelectedItem()
442       {
443          Query query = getSelectedQuery();
444          return query != null ? query.getResponse() : null;
445       }
446    }
447 
448    protected Query getSelectedQuery()
449    {
450       return queryDetailFormPresentationModel == null ? null :
451               queryDetailFormPresentationModel.getBean();
452    }
453 
454    private final class AddAction extends AbstractAction
455    {
456       public AddAction()
457       {
458          putValue( Action.SHORT_DESCRIPTION, "Adds a new Match" );
459          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/add_property.gif" ) );
460       }
461 
462       public void actionPerformed( ActionEvent e )
463       {
464          String name = UISupport.prompt( "Specify name for Match", "Add Query Match", "" );
465          if( name == null || name.trim().length() == 0 ) return;
466 
467          addQuery( name );
468       }
469    }
470 
471    private final class CopyAction extends AbstractAction
472    {
473       public CopyAction()
474       {
475          putValue( Action.SHORT_DESCRIPTION, "Copies the selected Match" );
476          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/clone_request.gif" ) );
477       }
478 
479       public void actionPerformed( ActionEvent e )
480       {
481          QueryMatchMockOperationDispatcher.Query selectedQuery = getSelectedQuery();
482          if( selectedQuery == null )
483             return;
484 
485          String name = UISupport.prompt( "Specify name for copied Query", "Copy Query", selectedQuery.getName() );
486          if( name == null || name.trim().length() == 0 ) return;
487 
488          QueryMatchMockOperationDispatcher.Query query = addQuery( name );
489          query.setMatch( selectedQuery.getMatch() );
490          query.setQuery( selectedQuery.getQuery() );
491          query.setResponse( selectedQuery.getResponse() );
492 
493          itemList.setSelectedIndex( getQueryCount() - 1 );
494       }
495    }
496 
497    private final class DeleteAction extends AbstractAction
498    {
499       public DeleteAction()
500       {
501          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/remove_property.gif" ) );
502          putValue( Action.SHORT_DESCRIPTION, "Deletes the selected Property Transfer" );
503       }
504 
505       public void actionPerformed( ActionEvent e )
506       {
507          QueryMatchMockOperationDispatcher.Query selectedQuery = getSelectedQuery();
508          if( selectedQuery == null )
509             return;
510 
511          if( UISupport.confirm( "Delete selected Query", "Delete Query" ) )
512          {
513             deleteQuery( selectedQuery );
514             if( getQueryCount() > 0 )
515                itemList.setSelectedIndex( 0 );
516          }
517       }
518    }
519 
520    private final class RenameAction extends AbstractAction
521    {
522       public RenameAction()
523       {
524          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/rename.gif" ) );
525          putValue( Action.SHORT_DESCRIPTION, "Renames the selected Property Transfer" );
526       }
527 
528       public void actionPerformed( ActionEvent e )
529       {
530          QueryMatchMockOperationDispatcher.Query selectedQuery = getSelectedQuery();
531          if( selectedQuery == null )
532             return;
533 
534          String newName = UISupport.prompt( "Specify new name for Query", "Rename Query", selectedQuery.getName() );
535 
536          if( newName != null && !selectedQuery.getName().equals( newName ) )
537          {
538             selectedQuery.setName( newName );
539             queryItemListModel.refresh();
540          }
541       }
542    }
543 
544    private final class DeclareNamespacesAction extends AbstractAction
545    {
546       public DeclareNamespacesAction()
547       {
548          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/declareNs.gif" ) );
549          putValue( Action.SHORT_DESCRIPTION, "Declare request namespaces in current query" );
550       }
551 
552       public void actionPerformed( ActionEvent e )
553       {
554          QueryMatchMockOperationDispatcher.Query selectedQuery = getSelectedQuery();
555          if( selectedQuery == null )
556             return;
557 
558          try
559          {
560             WsdlMockResult lastResult = getMockOperation().getLastMockResult();
561             String content = null;
562             if( lastResult == null )
563             {
564                if( !UISupport.confirm( "Missing last result, declare from default request instead?", "Declare Namespaces" ) )
565                {
566                   return;
567                }
568 
569                content = getMockOperation().getOperation().createRequest( true );
570             }
571             else
572             {
573                content = lastResult.getMockRequest().getRequestContent();
574             }
575 
576             String path = selectedQuery.getQuery();
577             if( path == null )
578                path = "";
579 
580             selectedQuery.setQuery( XmlUtils.declareXPathNamespaces( content ) + path );
581          }
582          catch( Exception e1 )
583          {
584             UISupport.showErrorMessage( e1 );
585          }
586       }
587    }
588 
589    private final class RunAction extends AbstractAction
590    {
591       public RunAction()
592       {
593          putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/run.gif" ) );
594          putValue( Action.SHORT_DESCRIPTION, "Runs Queries on last request" );
595       }
596 
597       public void actionPerformed( ActionEvent e )
598       {
599          WsdlMockResult result = getMockOperation().getLastMockResult();
600          if( result != null )
601          {
602             try
603             {
604                UISupport.showInfoMessage( "Selected [" + selectMockResponse( result.getMockRequest(), result ).getName() + "]" );
605             }
606             catch( DispatchException e1 )
607             {
608                UISupport.showErrorMessage( e1 );
609             }
610          }
611          else
612          {
613             UISupport.showErrorMessage( "Missing request to query" );
614          }
615       }
616    }
617 
618    private final class ExtractFromCurrentAction extends AbstractAction
619    {
620       public ExtractFromCurrentAction()
621       {
622          super( "Extract" );
623          putValue( Action.SHORT_DESCRIPTION, "Extracts the current value into the Value field" );
624       }
625 
626       public void actionPerformed( ActionEvent e )
627       {
628          QueryMatchMockOperationDispatcher.Query selectedQuery = getSelectedQuery();
629          if( selectedQuery == null )
630             return;
631 
632          WsdlMockResult result = getMockOperation().getLastMockResult();
633          String content;
634 
635          if( result != null && StringUtils.hasContent( result.getMockRequest().getRequestContent() ) )
636          {
637             content = result.getMockRequest().getRequestContent();
638          }
639          else
640          {
641             if( !UISupport.confirm( "Missing last result, extract from default request instead?", "Extract Match" ) )
642             {
643                return;
644             }
645 
646             content = getMockOperation().getOperation().createRequest( true );
647          }
648 
649          XmlCursor cursor = null;
650 
651          try
652          {
653             XmlObject xmlObject = XmlObject.Factory.parse( content );
654             cursor = xmlObject.newCursor();
655             cursor.selectPath( selectedQuery.getQuery() );
656             if( !cursor.toNextSelection() )
657             {
658                UISupport.showErrorMessage( "Missing match in request" );
659             }
660             else
661             {
662                selectedQuery.setMatch( XmlUtils.getValueForMatch( cursor ) );
663             }
664          }
665          catch( Throwable e1 )
666          {
667             SoapUI.logError( e1 );
668          }
669          finally
670          {
671             if( cursor != null )
672             {
673                cursor.dispose();
674             }
675          }
676       }
677    }
678 
679    private class QueryItemListCellRenderer extends DefaultListCellRenderer
680    {
681       private Color defaultForeground;
682 
683       private QueryItemListCellRenderer()
684       {
685          this.defaultForeground = getForeground();
686       }
687 
688       @Override
689       public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean cellHasFocus )
690       {
691          JLabel component = (JLabel) super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
692 
693          Query query = (Query) value;
694          component.setText( query.getName() );
695          component.setForeground( ( (Query) value ).isDisabled() ? Color.GRAY : defaultForeground );
696 
697          return component;
698       }
699    }
700 }