1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.impl.wsdl.mock;
14
15 import com.eviware.soapui.SoapUI;
16 import com.eviware.soapui.config.DispatchStyleConfig;
17 import com.eviware.soapui.config.DispatchStyleConfig.Enum;
18 import com.eviware.soapui.config.MockOperationConfig;
19 import com.eviware.soapui.config.MockResponseConfig;
20 import com.eviware.soapui.impl.wsdl.AbstractWsdlModelItem;
21 import com.eviware.soapui.impl.wsdl.WsdlInterface;
22 import com.eviware.soapui.impl.wsdl.WsdlOperation;
23 import com.eviware.soapui.impl.wsdl.support.CompressedStringSupport;
24 import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils;
25 import com.eviware.soapui.model.ModelItem;
26 import com.eviware.soapui.model.iface.Interface;
27 import com.eviware.soapui.model.iface.Operation;
28 import com.eviware.soapui.model.mock.MockOperation;
29 import com.eviware.soapui.model.mock.MockResponse;
30 import com.eviware.soapui.model.mock.MockRunContext;
31 import com.eviware.soapui.model.support.InterfaceListenerAdapter;
32 import com.eviware.soapui.model.support.ProjectListenerAdapter;
33 import com.eviware.soapui.settings.WsdlSettings;
34 import com.eviware.soapui.support.StringUtils;
35 import com.eviware.soapui.support.UISupport;
36 import com.eviware.soapui.support.scripting.ScriptEnginePool;
37 import com.eviware.soapui.support.scripting.SoapUIScriptEngine;
38 import com.eviware.soapui.support.xml.XmlUtils;
39 import org.apache.log4j.Logger;
40 import org.apache.xmlbeans.XmlCursor;
41 import org.apache.xmlbeans.XmlException;
42 import org.apache.xmlbeans.XmlObject;
43
44 import javax.swing.*;
45 import java.beans.PropertyChangeEvent;
46 import java.beans.PropertyChangeListener;
47 import java.util.ArrayList;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.concurrent.ConcurrentHashMap;
51 import java.util.concurrent.atomic.AtomicLong;
52
53 /***
54 * A WsdlMockOperation in a WsdlMockService
55 *
56 * @author ole.matzura
57 */
58
59 public class WsdlMockOperation extends AbstractWsdlModelItem<MockOperationConfig> implements MockOperation, PropertyChangeListener
60 {
61 private final static Logger log = Logger.getLogger(WsdlMockOperation.class);
62
63 public final static String DISPATCH_STYLE_PROPERTY = WsdlMockOperation.class.getName() + "@dispatchstyle";
64 public final static String DEFAULT_RESPONSE_PROPERTY = WsdlMockOperation.class.getName() + "@defaultresponse";
65 public final static String DISPATCH_PATH_PROPERTY = WsdlMockOperation.class.getName() + "@dispatchpath";
66 public final static String OPERATION_PROPERTY = WsdlMockOperation.class.getName() + "@operation";
67
68 private WsdlOperation operation;
69 private List<WsdlMockResponse> responses = new ArrayList<WsdlMockResponse>();
70 private int currentDispatchIndex;
71 private ScriptEnginePool scriptEnginePool;
72 private InternalInterfaceListener interfaceListener = new InternalInterfaceListener();
73 private InternalProjectListener projectListener = new InternalProjectListener();
74 private ImageIcon oneWayIcon;
75 private ImageIcon notificationIcon;
76 private ImageIcon solicitResponseIcon;
77
78 private AtomicLong counter;
79 private Map<QueryValuePair, WsdlMockResponse> responseMap;
80
81 public WsdlMockOperation(WsdlMockService mockService, MockOperationConfig config)
82 {
83 super( config, mockService, "/mockOperation.gif" );
84
85 Interface iface = mockService.getProject().getInterfaceByName( config.getInterface() );
86 if( iface == null )
87 {
88 SoapUI.log.warn( "Missing interface [" + config.getInterface() +
89 "] for MockOperation in project" );
90 }
91 else
92 {
93 operation = ( WsdlOperation ) iface.getOperationByName( config.getOperation() );
94 }
95
96 List<MockResponseConfig> responseConfigs = config.getResponseList();
97 for( MockResponseConfig responseConfig : responseConfigs )
98 {
99 WsdlMockResponse wsdlMockResponse = new WsdlMockResponse( this, responseConfig );
100 wsdlMockResponse.addPropertyChangeListener( this );
101 responses.add( wsdlMockResponse );
102 }
103
104 initData( config );
105
106 counter = new AtomicLong();
107 responseMap = new ConcurrentHashMap<QueryValuePair, WsdlMockResponse>();
108 }
109
110 private void initData( MockOperationConfig config )
111 {
112 if( !config.isSetName() )
113 config.setName( operation == null ? "<missing operation>" : operation.getName() );
114
115 if( !config.isSetDispatchStyle())
116 config.setDispatchStyle( DispatchStyleConfig.SEQUENCE );
117
118 if( !config.isSetDefaultResponse() && responses.size() > 0 )
119 setDefaultResponse( responses.get( 0 ).getName() );
120
121 scriptEnginePool = new ScriptEnginePool( this );
122 scriptEnginePool.setScript( getDispatchPath() );
123
124 if( operation != null )
125 {
126 operation.getInterface().getProject().addProjectListener( projectListener );
127 operation.getInterface().addInterfaceListener( interfaceListener );
128 operation.getInterface().addPropertyChangeListener( WsdlInterface.NAME_PROPERTY, this );
129 }
130
131 oneWayIcon = UISupport.createImageIcon( "/onewaymockoperation.gif" );
132 notificationIcon = UISupport.createImageIcon("/mocknotificationoperation.gif");
133 solicitResponseIcon = UISupport.createImageIcon("/mocksolicitresponseoperation.gif");
134 }
135
136 public WsdlMockOperation( WsdlMockService mockService, MockOperationConfig config, WsdlOperation operation )
137 {
138 super( config, mockService, "/mockOperation.gif" );
139 this.operation = operation;
140
141 config.setInterface( operation.getInterface().getName() );
142 config.setOperation( operation.getName() );
143
144 initData( config );
145 interfaceListener = new InternalInterfaceListener();
146
147 counter = new AtomicLong();
148 responseMap = new ConcurrentHashMap<QueryValuePair, WsdlMockResponse>();
149 }
150
151 @Override
152 public ImageIcon getIcon()
153 {
154 if (operation != null)
155 {
156 if (isOneWay())
157 {
158 return oneWayIcon;
159 }
160 else if (isNotification())
161 {
162 return notificationIcon;
163 }
164 else if (isSolicitResponse())
165 {
166 return solicitResponseIcon;
167 }
168 }
169
170 return super.getIcon();
171 }
172
173 public WsdlMockService getMockService()
174 {
175 return ( WsdlMockService ) getParent();
176 }
177
178 public WsdlMockResponse getMockResponseAt( int index )
179 {
180 return responses.get( index );
181 }
182
183 public WsdlOperation getOperation()
184 {
185 return operation;
186 }
187
188 public WsdlMockResponse getMockResponseByName( String name )
189 {
190 return ( WsdlMockResponse ) getWsdlModelItemByName( responses, name );
191 }
192
193 public int getMockResponseCount()
194 {
195 return responses.size();
196 }
197
198 public WsdlMockResponse addNewMockResponse( MockResponseConfig responseConfig )
199 {
200 WsdlMockResponse mockResponse = new WsdlMockResponse( this, responseConfig );
201
202 responses.add( mockResponse );
203 if( responses.size() == 1 )
204 setDefaultResponse( mockResponse.getName() );
205
206 (getMockService()).fireMockResponseAdded( mockResponse );
207
208
209 WsdlUtils.setDefaultWsaAction( mockResponse.getWsaConfig(), true );
210
211
212
213
214
215
216
217
218
219
220 return mockResponse;
221 }
222
223 public WsdlMockResponse addNewMockResponse( String name, boolean createResponse )
224 {
225 MockResponseConfig responseConfig = getConfig().addNewResponse();
226 responseConfig.setName( name );
227 responseConfig.addNewResponseContent();
228
229 if( createResponse && getOperation() != null && getOperation().isBidirectional() )
230 {
231 boolean createOptional = SoapUI.getSettings().getBoolean( WsdlSettings.XML_GENERATION_ALWAYS_INCLUDE_OPTIONAL_ELEMENTS );
232 CompressedStringSupport.setString( responseConfig.getResponseContent(), getOperation().createResponse( createOptional ));
233 }
234
235 return addNewMockResponse( responseConfig );
236 }
237
238 public WsdlMockResponse addNewMockResponse(String requestQuery, String matchingValue)
239 {
240
241 String name = String.valueOf(counter.addAndGet(1));
242
243
244 WsdlMockResponse mockResponse = addNewMockResponse(name, false);
245 responseMap.put(new QueryValuePair(requestQuery, matchingValue), mockResponse);
246 return mockResponse;
247 }
248
249 public void removeMockResponse( WsdlMockResponse mockResponse )
250 {
251 int ix = responses.indexOf( mockResponse );
252 responses.remove( ix );
253 mockResponse.removePropertyChangeListener( this );
254
255 try
256 {
257 (getMockService()).fireMockResponseRemoved( mockResponse );
258 }
259 finally
260 {
261 mockResponse.release();
262 getConfig().removeResponse( ix );
263 }
264 }
265
266 public WsdlMockResult dispatchRequest( WsdlMockRequest request ) throws DispatchException
267 {
268 try
269 {
270 request.setOperation( getOperation() );
271 WsdlMockResult result = new WsdlMockResult( request );
272
273 if( getMockResponseCount() == 0 )
274 throw new DispatchException( "Missing MockResponse(s) in MockOperation [" + getName() + "]" );
275
276 Enum dispatchStyle = getDispatchStyle();
277 if( dispatchStyle == DispatchStyleConfig.XPATH )
278 {
279 dispatchRequestXPath(request, result);
280 }
281 else if( dispatchStyle == DispatchStyleConfig.SCRIPT )
282 {
283 dispatchRequestScript(request, result);
284 }
285
286 else if( dispatchStyle == DispatchStyleConfig.QUERY_MATCH )
287 {
288 dispatchRequestQueryMatch(request, result);
289 }
290 else
291 {
292 dispatchRequestSequence(request, result);
293 }
294
295 return result;
296 }
297 catch( Throwable e )
298 {
299 throw new DispatchException( e );
300 }
301 }
302
303 private void dispatchRequestQueryMatch(WsdlMockRequest request, WsdlMockResult result) throws DispatchException
304 {
305 WsdlMockResponse mockResponse = getMatchingMockResponse(request);
306 if (mockResponse != null)
307 {
308 result.setMockResponse( mockResponse );
309 mockResponse.execute( request, result );
310 }
311 else
312 {
313 log.error("Unable to find a response for the request. Dropping it!");
314
315
316
317 }
318 }
319
320 private void dispatchRequestXPath(WsdlMockRequest request, WsdlMockResult result)
321 throws XmlException, DispatchException
322 {
323 XmlObject[] items = evaluateDispatchXPath( request );
324 for( XmlObject item : items )
325 {
326 WsdlMockResponse mockResponse = getMockResponseByName( XmlUtils.getNodeValue( item.getDomNode() ));
327
328 if( mockResponse == null )
329 mockResponse = getMockResponseByName( getDefaultResponse() );
330
331 if( mockResponse != null )
332 {
333 result.setMockResponse( mockResponse );
334 mockResponse.execute( request, result );
335 return;
336 }
337 }
338
339 throw new DispatchException( "Missing matching response message" );
340 }
341
342 private void dispatchRequestScript(WsdlMockRequest request, WsdlMockResult result)
343 throws DispatchException
344 {
345 Object retVal = evaluateDispatchScript( request );
346
347 WsdlMockResponse mockResponse = retVal == null ? getMockResponseByName( getDefaultResponse() )
348 : getMockResponseByName( retVal.toString() );
349
350 if( mockResponse == null )
351 mockResponse = getMockResponseByName( getDefaultResponse() );
352
353 if( mockResponse != null )
354 {
355 result.setMockResponse( mockResponse );
356 mockResponse.execute( request, result );
357 return;
358 }
359 else
360 {
361 throw new DispatchException( "Missing matching response message [" + retVal + "]" );
362 }
363 }
364
365 private void dispatchRequestSequence(WsdlMockRequest request, WsdlMockResult result)
366 throws DispatchException
367 {
368 WsdlMockResponse mockResponse = null;
369 synchronized( this )
370 {
371 if( getDispatchStyle() == DispatchStyleConfig.RANDOM )
372 {
373 currentDispatchIndex = ( int ) ( (Math.random() * getMockResponseCount()) + 0.5F );
374 }
375
376 if( currentDispatchIndex >= getMockResponseCount() )
377 currentDispatchIndex = 0;
378
379 mockResponse = getMockResponseAt( currentDispatchIndex );
380 result.setMockResponse( mockResponse );
381
382 currentDispatchIndex++;
383 }
384
385 mockResponse.execute( request, result );
386 }
387
388 @Override
389 public void release()
390 {
391 super.release();
392
393 for( WsdlMockResponse response : responses )
394 {
395 response.removePropertyChangeListener( this );
396 response.release();
397 }
398
399 scriptEnginePool.release();
400
401 if( operation != null )
402 {
403 operation.getInterface().getProject().removeProjectListener( projectListener );
404 operation.getInterface().removeInterfaceListener( interfaceListener );
405 operation.getInterface().removePropertyChangeListener( WsdlInterface.NAME_PROPERTY, this );
406 }
407 }
408
409 public XmlObject[] evaluateDispatchXPath( WsdlMockRequest request ) throws XmlException
410 {
411 XmlObject xmlObject = request.getRequestXmlObject();
412 String path = getDispatchPath();
413 if( StringUtils.isNullOrEmpty(path))
414 throw new XmlException( "Missing dispatch XPath expression" );
415
416 XmlObject[] items = xmlObject.selectPath( path );
417 return items;
418 }
419
420 public Object evaluateDispatchScript( WsdlMockRequest request ) throws DispatchException
421 {
422 String dispatchPath = getDispatchPath();
423 if( dispatchPath == null || dispatchPath.trim().length() == 0 )
424 {
425 throw new DispatchException( "Dispatch Script is empty" );
426 }
427
428 SoapUIScriptEngine scriptEngine = scriptEnginePool.getScriptEngine();
429
430 try
431 {
432 WsdlMockService mockService = getMockService();
433 WsdlMockRunner mockRunner = mockService.getMockRunner();
434 MockRunContext context = mockRunner == null ? new WsdlMockRunContext( mockService, null ) : mockRunner.getMockContext();
435
436 scriptEngine.setVariable( "context", context );
437 scriptEngine.setVariable( "requestContext", request == null ? null : request.getRequestContext() );
438 scriptEngine.setVariable( "mockRequest", request);
439 scriptEngine.setVariable( "mockOperation", this );
440 scriptEngine.setVariable( "log", SoapUI.ensureGroovyLog() );
441
442 scriptEngine.setScript( dispatchPath );
443 Object retVal = scriptEngine.run();
444 return retVal;
445 }
446 catch( Throwable e )
447 {
448 SoapUI.logError( e );
449 throw new DispatchException( "Failed to dispatch using script; " + e );
450 }
451 finally
452 {
453 scriptEnginePool.returnScriptEngine( scriptEngine );
454 }
455 }
456
457 public DispatchStyleConfig.Enum getDispatchStyle()
458 {
459 return getConfig().isSetDispatchStyle() ? getConfig().getDispatchStyle() : DispatchStyleConfig.SEQUENCE;
460 }
461
462 public void setDispatchStyle( DispatchStyleConfig.Enum dispatchStyle )
463 {
464 Enum old = getDispatchStyle();
465 getConfig().setDispatchStyle( dispatchStyle );
466 notifyPropertyChanged( DISPATCH_STYLE_PROPERTY, old, dispatchStyle );
467 }
468
469 public String getDispatchPath()
470 {
471 return getConfig().getDispatchPath();
472 }
473
474 public void setDispatchPath( String dispatchPath )
475 {
476 String old = getDispatchPath();
477 getConfig().setDispatchPath( dispatchPath );
478 notifyPropertyChanged( DISPATCH_PATH_PROPERTY, old, dispatchPath );
479
480 scriptEnginePool.setScript( dispatchPath );
481 }
482
483 public String getWsdlOperationName()
484 {
485 return operation == null ? null : operation.getName();
486 }
487
488 public String getDefaultResponse()
489 {
490 return getConfig().getDefaultResponse();
491 }
492
493 public void setDefaultResponse( String defaultResponse )
494 {
495 String old = getDefaultResponse();
496 getConfig().setDefaultResponse( defaultResponse );
497 notifyPropertyChanged( DEFAULT_RESPONSE_PROPERTY, old, defaultResponse );
498 }
499
500 public List<MockResponse> getMockResponses()
501 {
502 return new ArrayList<MockResponse>( responses );
503 }
504
505 public void propertyChange( PropertyChangeEvent arg0 )
506 {
507 if( arg0.getPropertyName().equals( WsdlMockResponse.NAME_PROPERTY ))
508 {
509 if( arg0.getOldValue().equals( getDefaultResponse() ))
510 setDefaultResponse( arg0.getNewValue().toString() );
511 }
512 else if( arg0.getPropertyName().equals( WsdlInterface.NAME_PROPERTY ))
513 {
514 getConfig().setInterface( arg0.getNewValue().toString() );
515 }
516 }
517
518 public WsdlMockResult getLastMockResult()
519 {
520 WsdlMockResult result = null;
521
522 for( WsdlMockResponse response : responses )
523 {
524 WsdlMockResult mockResult = response.getMockResult();
525 if( mockResult != null )
526 {
527 if( result == null || result.getTimestamp() > mockResult.getTimestamp() )
528 result = mockResult;
529 }
530 }
531
532 return result;
533 }
534
535 public void setOperation( WsdlOperation operation )
536 {
537 WsdlOperation oldOperation = getOperation();
538
539 if( operation == null )
540 {
541 getConfig().unsetInterface();
542 getConfig().unsetOperation();
543 }
544 else
545 {
546 getConfig().setInterface( operation.getInterface().getName() );
547 getConfig().setOperation( operation.getName() );
548 }
549
550 this.operation = operation;
551
552 notifyPropertyChanged( OPERATION_PROPERTY, oldOperation, operation );
553 }
554
555 @Override
556 public void beforeSave()
557 {
558 for( WsdlMockResponse mockResponse : responses )
559 mockResponse.beforeSave();
560 }
561
562 private class InternalInterfaceListener extends InterfaceListenerAdapter
563 {
564 @Override
565 public void operationUpdated( Operation operation )
566 {
567 if( operation == WsdlMockOperation.this.operation )
568 getConfig().setOperation( operation.getName() );
569 }
570
571 @Override
572 public void operationRemoved( Operation operation )
573 {
574 if( operation == WsdlMockOperation.this.operation )
575 getMockService().removeMockOperation( WsdlMockOperation.this );
576 }
577 }
578
579 private class InternalProjectListener extends ProjectListenerAdapter
580 {
581 @Override
582 public void interfaceRemoved( Interface iface )
583 {
584 if( operation.getInterface() == iface )
585 getMockService().removeMockOperation( WsdlMockOperation.this );
586 }
587
588 @Override
589 public void interfaceUpdated(Interface iface)
590 {
591 if( operation.getInterface() == iface )
592 getConfig().setInterface( iface.getName() );
593 }
594 }
595
596 public boolean isOneWay()
597 {
598 return operation == null ? false : operation.isOneWay();
599 }
600
601 public boolean isNotification()
602 {
603 return operation == null ? false : operation.isNotification();
604 }
605
606 public boolean isSolicitResponse()
607 {
608 return operation == null ? false : operation.isSolicitResponse();
609 }
610
611 public boolean isUnidirectional()
612 {
613 return operation == null ? false : operation.isUnidirectional();
614 }
615
616 public boolean isBidirectional()
617 {
618 return !isUnidirectional();
619 }
620
621 public List<? extends ModelItem> getChildren()
622 {
623 return responses;
624 }
625
626 public void onStart()
627 {
628 currentDispatchIndex = 0;
629 }
630
631 private WsdlMockResponse getMatchingMockResponse(WsdlMockRequest request)
632 throws DispatchException
633 {
634 try
635 {
636 XmlObject xmlObject = request.getRequestXmlObject();
637
638 for (QueryValuePair pair : responseMap.keySet())
639 {
640 log.debug("Testing request for match: " + pair);
641
642 XmlObject[] nodes = xmlObject.selectPath(pair.getQuery());
643 if (nodes != null && nodes.length > 0)
644 {
645
646 log.debug("Comparing selected node " + nodes[0] +
647 " with the value " + pair.getValue());
648 XmlCursor cursor = nodes[0].newCursor();
649 try
650 {
651 if (pair.getValue().equals(cursor.getTextValue()))
652 {
653 log.debug("Found a request with a matching query: +" +
654 request.getRequestContent());
655
656 return responseMap.get(pair);
657 }
658 }
659 finally
660 {
661 cursor.dispose();
662 }
663 }
664 }
665
666 return null;
667
668 }
669 catch (XmlException e)
670 {
671 throw new DispatchException(e);
672 }
673 }
674 }