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