1
2
3
4
5
6
7
8
9
10
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( 5 );
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 }