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 buildEditorComponent()
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 addlQueryToolbarActions( 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 addlQueryToolbarActions( 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 return getMockOperation().getMockResponseByName( query.getResponse() );
253 }
254 }
255
256 cursorCache.put( path, cursor );
257 }
258 }
259
260 return null;
261 }
262 catch( Throwable e )
263 {
264 throw new DispatchException( e );
265 }
266 finally
267 {
268 for( XmlCursor cursor : cursorCache.values() )
269 {
270 if( cursor != null )
271 {
272 cursor.dispose();
273 }
274 }
275 }
276 }
277
278 public Query addQuery( String name )
279 {
280 Query query = new Query( conf.addNewQuery() );
281 query.setName( name );
282 queries.add( query );
283
284 getPropertyChangeSupport().firePropertyChange( "queries", null, query );
285
286 if( queryItemListModel != null )
287 queryItemListModel.fireAdded();
288
289 return query;
290 }
291
292 public void deleteQuery( Query query )
293 {
294 int ix = queries.indexOf( query );
295 queries.remove( ix );
296 getPropertyChangeSupport().firePropertyChange( "queries", query, null );
297
298 if( queryItemListModel != null )
299 queryItemListModel.fireRemoved( ix );
300
301 conf.removeQuery( ix );
302 saveConfig();
303 }
304
305 public Query[] getQueries()
306 {
307 return queries.toArray( new Query[queries.size()] );
308 }
309
310 public int getQueryCount()
311 {
312 return queries.size();
313 }
314
315 public Query getQueryAt( int index )
316 {
317 return queries.get( index );
318 }
319
320 public void propertyChange( PropertyChangeEvent evt )
321 {
322 if( queryItemListModel != null )
323 queryItemListModel.refresh();
324 }
325
326 public static class Factory implements MockOperationDispatchFactory
327 {
328 public MockOperationDispatcher build( WsdlMockOperation mockOperation )
329 {
330 return new QueryMatchMockOperationDispatcher( mockOperation );
331 }
332 }
333
334 public class Query extends AbstractPropertyChangeNotifier
335 {
336 private MockOperationQueryMatchDispatchConfig.Query config;
337
338 protected Query( MockOperationQueryMatchDispatchConfig.Query config )
339 {
340 this.config = config;
341 }
342
343 public String getName()
344 {
345 return config.getName();
346 }
347
348 public void setName( String s )
349 {
350 String old = config.getName();
351 config.setName( s );
352 saveConfig();
353 firePropertyChange( "name", old, s );
354 }
355
356 public boolean isDisabled()
357 {
358 return config.getDisabled();
359 }
360
361 public void setDisabled( boolean disabled )
362 {
363 boolean old = config.getDisabled();
364 if( old == disabled )
365 return;
366 config.setDisabled( disabled );
367 saveConfig();
368 firePropertyChange( "disabled", old, disabled );
369 queryItemListModel.refresh();
370 }
371
372 public String getQuery()
373 {
374 return config.getQuery();
375 }
376
377 public void setQuery( String s )
378 {
379 String old = config.getQuery();
380 config.setQuery( s );
381 saveConfig();
382 firePropertyChange( "query", old, s );
383 }
384
385 public String getMatch()
386 {
387 return config.getMatch();
388 }
389
390 public void setMatch( String s )
391 {
392 String old = config.getMatch();
393 config.setMatch( s );
394 saveConfig();
395 firePropertyChange( "match", old, s );
396 }
397
398 public String getResponse()
399 {
400 return config.getResponse();
401 }
402
403 public void setResponse( String s )
404 {
405 String old = config.getResponse();
406 config.setResponse( s );
407 saveConfig();
408 firePropertyChange( "response", old, s );
409 }
410 }
411
412 private void saveConfig()
413 {
414 saveConfig( conf );
415 }
416
417 private class QueryItemListModel extends AbstractListModel
418 {
419 public int getSize()
420 {
421 return getQueryCount();
422 }
423
424 public Object getElementAt( int index )
425 {
426 return getQueryAt( index );
427 }
428
429 public void refresh()
430 {
431 fireContentsChanged( this, 0, getQueryCount() );
432 }
433
434 public void fireAdded()
435 {
436 fireIntervalAdded( this, getQueryCount(), getQueryCount() );
437 }
438
439 public void fireRemoved( int index )
440 {
441 fireIntervalRemoved( this, index, index );
442 }
443 }
444
445 private class MockResponsesComboBoxModel extends AbstractListModel implements ComboBoxModel
446 {
447 public int getSize()
448 {
449 return getMockOperation().getMockResponseCount();
450 }
451
452 public Object getElementAt( int index )
453 {
454 return getMockOperation().getMockResponseAt( index ).getName();
455 }
456
457 public void setSelectedItem( Object anItem )
458 {
459 Query query = getSelectedQuery();
460 if( query != null )
461 query.setResponse( String.valueOf( anItem ) );
462 }
463
464 public Object getSelectedItem()
465 {
466 Query query = getSelectedQuery();
467 return query != null ? query.getResponse() : null;
468 }
469 }
470
471 protected Query getSelectedQuery()
472 {
473 return queryDetailFormPresentationModel == null ? null : queryDetailFormPresentationModel.getBean();
474 }
475
476 private final class AddAction extends AbstractAction
477 {
478 public AddAction()
479 {
480 putValue( Action.SHORT_DESCRIPTION, "Adds a new Match" );
481 putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/add_property.gif" ) );
482 }
483
484 public void actionPerformed( ActionEvent e )
485 {
486 String name = UISupport.prompt( "Specify name for Match", "Add Query Match", "" );
487 if( name == null || name.trim().length() == 0 )
488 return;
489
490 addQuery( name );
491 }
492 }
493
494 private final class CopyAction extends AbstractAction
495 {
496 public CopyAction()
497 {
498 putValue( Action.SHORT_DESCRIPTION, "Copies the selected Match" );
499 putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/clone_request.gif" ) );
500 }
501
502 public void actionPerformed( ActionEvent e )
503 {
504 QueryMatchMockOperationDispatcher.Query selectedQuery = getSelectedQuery();
505 if( selectedQuery == null )
506 return;
507
508 String name = UISupport.prompt( "Specify name for copied Query", "Copy Query", selectedQuery.getName() );
509 if( name == null || name.trim().length() == 0 )
510 return;
511
512 QueryMatchMockOperationDispatcher.Query query = addQuery( name );
513 query.setMatch( selectedQuery.getMatch() );
514 query.setQuery( selectedQuery.getQuery() );
515 query.setResponse( selectedQuery.getResponse() );
516
517 itemList.setSelectedIndex( getQueryCount() - 1 );
518 }
519 }
520
521 private final class DeleteAction extends AbstractAction
522 {
523 public DeleteAction()
524 {
525 putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/remove_property.gif" ) );
526 putValue( Action.SHORT_DESCRIPTION, "Deletes the selected Property Transfer" );
527 }
528
529 public void actionPerformed( ActionEvent e )
530 {
531 QueryMatchMockOperationDispatcher.Query selectedQuery = getSelectedQuery();
532 if( selectedQuery == null )
533 return;
534
535 if( UISupport.confirm( "Delete selected Query", "Delete Query" ) )
536 {
537 deleteQuery( selectedQuery );
538 if( getQueryCount() > 0 )
539 itemList.setSelectedIndex( 0 );
540 }
541 }
542 }
543
544 private final class RenameAction extends AbstractAction
545 {
546 public RenameAction()
547 {
548 putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/rename.gif" ) );
549 putValue( Action.SHORT_DESCRIPTION, "Renames the selected Property Transfer" );
550 }
551
552 public void actionPerformed( ActionEvent e )
553 {
554 QueryMatchMockOperationDispatcher.Query selectedQuery = getSelectedQuery();
555 if( selectedQuery == null )
556 return;
557
558 String newName = UISupport.prompt( "Specify new name for Query", "Rename Query", selectedQuery.getName() );
559
560 if( newName != null && !selectedQuery.getName().equals( newName ) )
561 {
562 selectedQuery.setName( newName );
563 queryItemListModel.refresh();
564 }
565 }
566 }
567
568 private final class DeclareNamespacesAction extends AbstractAction
569 {
570 public DeclareNamespacesAction()
571 {
572 putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/declareNs.gif" ) );
573 putValue( Action.SHORT_DESCRIPTION, "Declare request namespaces in current query" );
574 }
575
576 public void actionPerformed( ActionEvent e )
577 {
578 QueryMatchMockOperationDispatcher.Query selectedQuery = getSelectedQuery();
579 if( selectedQuery == null )
580 return;
581
582 try
583 {
584 WsdlMockResult lastResult = getMockOperation().getLastMockResult();
585 String content = null;
586 if( lastResult == null )
587 {
588 if( !UISupport.confirm( "Missing last result, declare from default request instead?",
589 "Declare Namespaces" ) )
590 {
591 return;
592 }
593
594 content = getMockOperation().getOperation().createRequest( true );
595 }
596 else
597 {
598 content = lastResult.getMockRequest().getRequestContent();
599 }
600
601 String path = selectedQuery.getQuery();
602 if( path == null )
603 path = "";
604
605 selectedQuery.setQuery( XmlUtils.declareXPathNamespaces( content ) + path );
606 }
607 catch( Exception e1 )
608 {
609 UISupport.showErrorMessage( e1 );
610 }
611 }
612 }
613
614 private final class RunAction extends AbstractAction
615 {
616 public RunAction()
617 {
618 putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/run.gif" ) );
619 putValue( Action.SHORT_DESCRIPTION, "Runs Queries on last request" );
620 }
621
622 public void actionPerformed( ActionEvent e )
623 {
624 WsdlMockResult result = getMockOperation().getLastMockResult();
625 if( result != null )
626 {
627 try
628 {
629 UISupport.showInfoMessage( "Selected ["
630 + selectMockResponse( result.getMockRequest(), result ).getName() + "]" );
631 }
632 catch( DispatchException e1 )
633 {
634 UISupport.showErrorMessage( e1 );
635 }
636 }
637 else
638 {
639 UISupport.showErrorMessage( "Missing request to query" );
640 }
641 }
642 }
643
644 private final class ExtractFromCurrentAction extends AbstractAction
645 {
646 public ExtractFromCurrentAction()
647 {
648 super( "Extract" );
649 putValue( Action.SHORT_DESCRIPTION, "Extracts the current value into the Value field" );
650 }
651
652 public void actionPerformed( ActionEvent e )
653 {
654 QueryMatchMockOperationDispatcher.Query selectedQuery = getSelectedQuery();
655 if( selectedQuery == null )
656 return;
657
658 WsdlMockResult result = getMockOperation().getLastMockResult();
659 String content;
660
661 if( result != null && StringUtils.hasContent( result.getMockRequest().getRequestContent() ) )
662 {
663 content = result.getMockRequest().getRequestContent();
664 }
665 else
666 {
667 if( !UISupport.confirm( "Missing last result, extract from default request instead?", "Extract Match" ) )
668 {
669 return;
670 }
671
672 content = getMockOperation().getOperation().createRequest( true );
673 }
674
675 XmlCursor cursor = null;
676
677 try
678 {
679 XmlObject xmlObject = XmlObject.Factory.parse( content );
680 cursor = xmlObject.newCursor();
681 cursor.selectPath( selectedQuery.getQuery() );
682 if( !cursor.toNextSelection() )
683 {
684 UISupport.showErrorMessage( "Missing match in request" );
685 }
686 else
687 {
688 selectedQuery.setMatch( XmlUtils.getValueForMatch( cursor ) );
689 }
690 }
691 catch( Throwable e1 )
692 {
693 SoapUI.logError( e1 );
694 }
695 finally
696 {
697 if( cursor != null )
698 {
699 cursor.dispose();
700 }
701 }
702 }
703 }
704
705 private class QueryItemListCellRenderer extends DefaultListCellRenderer
706 {
707 private Color defaultForeground;
708
709 private QueryItemListCellRenderer()
710 {
711 this.defaultForeground = getForeground();
712 }
713
714 @Override
715 public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected,
716 boolean cellHasFocus )
717 {
718 JLabel component = ( JLabel )super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus );
719
720 Query query = ( Query )value;
721 component.setText( query.getName() );
722 component.setForeground( ( ( Query )value ).isDisabled() ? Color.GRAY : defaultForeground );
723
724 return component;
725 }
726 }
727 }