1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.impl.wsdl.mock;
14
15 import java.beans.PropertyChangeEvent;
16 import java.beans.PropertyChangeListener;
17 import java.util.ArrayList;
18 import java.util.List;
19
20 import javax.servlet.http.HttpServletResponse;
21 import javax.swing.ImageIcon;
22
23 import org.apache.xmlbeans.XmlException;
24 import org.apache.xmlbeans.XmlObject;
25
26 import com.eviware.soapui.SoapUI;
27 import com.eviware.soapui.config.DispatchStyleConfig;
28 import com.eviware.soapui.config.MockOperationConfig;
29 import com.eviware.soapui.config.MockResponseConfig;
30 import com.eviware.soapui.config.DispatchStyleConfig.Enum;
31 import com.eviware.soapui.impl.wsdl.AbstractWsdlModelItem;
32 import com.eviware.soapui.impl.wsdl.WsdlInterface;
33 import com.eviware.soapui.impl.wsdl.WsdlOperation;
34 import com.eviware.soapui.impl.wsdl.support.CompressedStringSupport;
35 import com.eviware.soapui.model.ModelItem;
36 import com.eviware.soapui.model.iface.Interface;
37 import com.eviware.soapui.model.iface.Operation;
38 import com.eviware.soapui.model.mock.MockOperation;
39 import com.eviware.soapui.model.mock.MockResponse;
40 import com.eviware.soapui.model.mock.MockRunContext;
41 import com.eviware.soapui.model.support.InterfaceListenerAdapter;
42 import com.eviware.soapui.model.support.ProjectListenerAdapter;
43 import com.eviware.soapui.settings.WsdlSettings;
44 import com.eviware.soapui.support.UISupport;
45 import com.eviware.soapui.support.scripting.ScriptEnginePool;
46 import com.eviware.soapui.support.scripting.SoapUIScriptEngine;
47 import com.eviware.soapui.support.xml.XmlUtils;
48
49 /***
50 * A WsdlMockOperation in a WsdlMockService
51 *
52 * @author ole.matzura
53 */
54
55 public class WsdlMockOperation extends AbstractWsdlModelItem<MockOperationConfig> implements MockOperation, PropertyChangeListener
56 {
57 public final static String DISPATCH_STYLE_PROPERTY = WsdlMockOperation.class.getName() + "@dispatchstyle";
58 public final static String DEFAULT_RESPONSE_PROPERTY = WsdlMockOperation.class.getName() + "@defaultresponse";
59 public final static String DISPATCH_PATH_PROPERTY = WsdlMockOperation.class.getName() + "@dispatchpath";
60 public final static String OPERATION_PROPERTY = WsdlMockOperation.class.getName() + "@operation";
61
62 private WsdlOperation operation;
63 private List<WsdlMockResponse> responses = new ArrayList<WsdlMockResponse>();
64 private int currentDispatchIndex;
65 private ScriptEnginePool scriptEnginePool;
66 private InternalInterfaceListener interfaceListener = new InternalInterfaceListener();
67 private InternalProjectListener projectListener = new InternalProjectListener();
68 private ImageIcon oneWayIcon;
69
70 public WsdlMockOperation(WsdlMockService mockService, MockOperationConfig config)
71 {
72 super( config, mockService, "/mockOperation.gif" );
73
74 Interface iface = mockService.getProject().getInterfaceByName( config.getInterface() );
75 if( iface == null )
76 {
77 SoapUI.log.warn( "Missing interface [" + config.getInterface() +
78 "] for MockOperation in project" );
79 }
80 else
81 {
82 operation = ( WsdlOperation ) iface.getOperationByName( config.getOperation() );
83 }
84
85 List<MockResponseConfig> responseConfigs = config.getResponseList();
86 for( MockResponseConfig responseConfig : responseConfigs )
87 {
88 WsdlMockResponse wsdlMockResponse = new WsdlMockResponse( this, responseConfig );
89 wsdlMockResponse.addPropertyChangeListener( this );
90 responses.add( wsdlMockResponse );
91 }
92
93 initData( config );
94 }
95
96 private void initData( MockOperationConfig config )
97 {
98 if( !config.isSetName() )
99 config.setName( operation == null ? "<missing operation>" : operation.getName() );
100
101 if( !config.isSetDispatchStyle())
102 config.setDispatchStyle( DispatchStyleConfig.SEQUENCE );
103
104 if( !config.isSetDefaultResponse() && responses.size() > 0 )
105 setDefaultResponse( responses.get( 0 ).getName() );
106
107 scriptEnginePool = new ScriptEnginePool( this );
108 scriptEnginePool.setScript( getDispatchPath() );
109
110 if( operation != null )
111 {
112 operation.getInterface().getProject().addProjectListener( projectListener );
113 operation.getInterface().addInterfaceListener( interfaceListener );
114 operation.getInterface().addPropertyChangeListener( WsdlInterface.NAME_PROPERTY, this );
115 }
116
117 oneWayIcon = UISupport.createImageIcon( "/onewaymockoperation.gif" );
118 }
119
120 public WsdlMockOperation( WsdlMockService mockService, MockOperationConfig config, WsdlOperation operation )
121 {
122 super( config, mockService, "/mockOperation.gif" );
123 this.operation = operation;
124
125 config.setInterface( operation.getInterface().getName() );
126 config.setOperation( operation.getName() );
127
128 initData( config );
129 }
130
131 @Override
132 public ImageIcon getIcon()
133 {
134 if( operation != null && operation.isOneWay() )
135 return oneWayIcon;
136 else
137 return super.getIcon();
138 }
139
140 public WsdlMockService getMockService()
141 {
142 return ( WsdlMockService ) getParent();
143 }
144
145 public WsdlMockResponse getMockResponseAt( int index )
146 {
147 return responses.get( index );
148 }
149
150 public WsdlOperation getOperation()
151 {
152 return operation;
153 }
154
155 public WsdlMockResponse getMockResponseByName( String name )
156 {
157 return ( WsdlMockResponse ) getWsdlModelItemByName( responses, name );
158 }
159
160 public int getMockResponseCount()
161 {
162 return responses.size();
163 }
164
165 public WsdlMockResponse addNewMockResponse( MockResponseConfig responseConfig )
166 {
167 WsdlMockResponse mockResponse = new WsdlMockResponse( this, responseConfig );
168
169 responses.add( mockResponse );
170 if( responses.size() == 1 )
171 setDefaultResponse( mockResponse.getName() );
172
173 (getMockService()).fireMockResponseAdded( mockResponse );
174
175 return mockResponse;
176 }
177
178 public WsdlMockResponse addNewMockResponse( String name, boolean createResponse )
179 {
180 MockResponseConfig responseConfig = getConfig().addNewResponse();
181 responseConfig.setName( name );
182 responseConfig.addNewResponseContent();
183
184 if( createResponse && getOperation() != null && !getOperation().isOneWay())
185 {
186 boolean createOptional = SoapUI.getSettings().getBoolean( WsdlSettings.XML_GENERATION_ALWAYS_INCLUDE_OPTIONAL_ELEMENTS );
187 CompressedStringSupport.setString( responseConfig.getResponseContent(), getOperation().createResponse( createOptional ));
188 }
189
190 return addNewMockResponse( responseConfig );
191 }
192
193 public void removeMockResponse( WsdlMockResponse mockResponse )
194 {
195 int ix = responses.indexOf( mockResponse );
196 responses.remove( ix );
197 mockResponse.removePropertyChangeListener( this );
198
199 try
200 {
201 (getMockService()).fireMockResponseRemoved( mockResponse );
202 }
203 finally
204 {
205 mockResponse.release();
206 getConfig().removeResponse( ix );
207 }
208 }
209
210 public WsdlMockResult dispatchRequest( WsdlMockRequest request, HttpServletResponse response ) throws DispatchException
211 {
212 try
213 {
214 request.setOperation( getOperation() );
215 WsdlMockResult result = new WsdlMockResult( request, response );
216
217 if( getMockResponseCount() == 0 )
218 throw new DispatchException( "Missing MockResponse(s) in MockOperation [" + getName() + "]" );
219
220 if( getDispatchStyle() == DispatchStyleConfig.XPATH )
221 {
222 XmlObject[] items = evaluateDispatchXPath( request );
223 for( XmlObject item : items )
224 {
225 WsdlMockResponse mockResponse = getMockResponseByName( XmlUtils.getNodeValue( item.getDomNode() ));
226
227 if( mockResponse == null )
228 mockResponse = getMockResponseByName( getDefaultResponse() );
229
230 if( mockResponse != null )
231 {
232 result.setMockResponse( mockResponse );
233 mockResponse.execute( request, result );
234
235 return result;
236 }
237 }
238
239 throw new DispatchException( "Missing matching response message" );
240 }
241 else if( getDispatchStyle() == DispatchStyleConfig.SCRIPT )
242 {
243 Object retVal = evaluateDispatchScript( request );
244
245 WsdlMockResponse mockResponse = retVal == null ? getMockResponseByName( getDefaultResponse() )
246 : getMockResponseByName( retVal.toString() );
247
248 if( mockResponse != null )
249 {
250 result.setMockResponse( mockResponse );
251 mockResponse.execute( request, result );
252
253 return result;
254 }
255 else
256 {
257 throw new DispatchException( "Missing matching response message [" + retVal + "]" );
258 }
259 }
260 else
261 {
262 WsdlMockResponse mockResponse = null;
263 synchronized( this )
264 {
265 if( getDispatchStyle() == DispatchStyleConfig.RANDOM )
266 {
267 currentDispatchIndex = ( int ) ( (Math.random() * getMockResponseCount()) + 0.5F );
268 }
269
270 if( currentDispatchIndex >= getMockResponseCount() )
271 currentDispatchIndex = 0;
272
273 mockResponse = getMockResponseAt( currentDispatchIndex );
274 result.setMockResponse( mockResponse );
275
276 currentDispatchIndex++;
277 }
278
279 mockResponse.execute( request, result );
280 }
281
282 return result;
283 }
284 catch( Exception e )
285 {
286 throw new DispatchException( e );
287 }
288 }
289
290 @Override
291 public void release()
292 {
293 super.release();
294
295 for( WsdlMockResponse response : responses )
296 {
297 response.removePropertyChangeListener( this );
298 response.release();
299 }
300
301 scriptEnginePool.release();
302
303 if( operation != null )
304 {
305 operation.getInterface().getProject().removeProjectListener( projectListener );
306 operation.getInterface().removeInterfaceListener( interfaceListener );
307 operation.getInterface().removePropertyChangeListener( WsdlInterface.NAME_PROPERTY, this );
308 }
309 }
310
311 public XmlObject[] evaluateDispatchXPath( WsdlMockRequest request ) throws XmlException
312 {
313 XmlObject xmlObject = request.getRequestXmlObject();
314 XmlObject[] items = xmlObject.selectPath( getDispatchPath() );
315 return items;
316 }
317
318 public Object evaluateDispatchScript( WsdlMockRequest request ) throws DispatchException
319 {
320 String dispatchPath = getDispatchPath();
321 if( dispatchPath == null || dispatchPath.trim().length() == 0 )
322 {
323 throw new DispatchException( "Dispatch Script is empty" );
324 }
325
326 SoapUIScriptEngine scriptEngine = scriptEnginePool.getScriptEngine();
327
328 try
329 {
330 WsdlMockService mockService = getMockService();
331 WsdlMockRunner mockRunner = mockService.getMockRunner();
332 MockRunContext context = mockRunner == null ? new WsdlMockRunContext( mockService, null ) : mockRunner.getMockContext();
333
334 scriptEngine.setVariable( "context", context );
335 scriptEngine.setVariable( "requestContext", request == null ? null : request.getRequestContext() );
336 scriptEngine.setVariable( "mockRequest", request);
337 scriptEngine.setVariable( "mockOperation", this );
338 scriptEngine.setVariable( "log", SoapUI.ensureGroovyLog() );
339
340 scriptEngine.setScript( dispatchPath );
341 Object retVal = scriptEngine.run();
342 return retVal;
343 }
344 catch( Throwable e )
345 {
346 SoapUI.logError( e );
347 throw new DispatchException( "Failed to dispatch using script; " + e );
348 }
349 finally
350 {
351 scriptEnginePool.returnScriptEngine( scriptEngine );
352 }
353 }
354
355 public DispatchStyleConfig.Enum getDispatchStyle()
356 {
357 return getConfig().isSetDispatchStyle() ? getConfig().getDispatchStyle() : DispatchStyleConfig.SEQUENCE;
358 }
359
360 public void setDispatchStyle( DispatchStyleConfig.Enum dispatchStyle )
361 {
362 Enum old = getDispatchStyle();
363 getConfig().setDispatchStyle( dispatchStyle );
364 notifyPropertyChanged( DISPATCH_STYLE_PROPERTY, old, dispatchStyle );
365 }
366
367 public String getDispatchPath()
368 {
369 return getConfig().getDispatchPath();
370 }
371
372 public void setDispatchPath( String dispatchPath )
373 {
374 String old = getDispatchPath();
375 getConfig().setDispatchPath( dispatchPath );
376 notifyPropertyChanged( DISPATCH_PATH_PROPERTY, old, dispatchPath );
377
378 scriptEnginePool.setScript( dispatchPath );
379 }
380
381 public String getWsdlOperationName()
382 {
383 return operation == null ? null : operation.getName();
384 }
385
386 public String getDefaultResponse()
387 {
388 return getConfig().getDefaultResponse();
389 }
390
391 public void setDefaultResponse( String defaultResponse )
392 {
393 String old = getDefaultResponse();
394 getConfig().setDefaultResponse( defaultResponse );
395 notifyPropertyChanged( DEFAULT_RESPONSE_PROPERTY, old, defaultResponse );
396 }
397
398 public List<MockResponse> getMockResponses()
399 {
400 return new ArrayList<MockResponse>( responses );
401 }
402
403 public void propertyChange( PropertyChangeEvent arg0 )
404 {
405 if( arg0.getPropertyName().equals( WsdlMockResponse.NAME_PROPERTY ))
406 {
407 if( arg0.getOldValue().equals( getDefaultResponse() ))
408 setDefaultResponse( arg0.getNewValue().toString() );
409 }
410 else if( arg0.getPropertyName().equals( WsdlInterface.NAME_PROPERTY ))
411 {
412 getConfig().setInterface( arg0.getNewValue().toString() );
413 }
414 }
415
416 public WsdlMockResult getLastMockResult()
417 {
418 WsdlMockResult result = null;
419
420 for( WsdlMockResponse response : responses )
421 {
422 WsdlMockResult mockResult = response.getMockResult();
423 if( mockResult != null )
424 {
425 if( result == null || result.getTimestamp() > mockResult.getTimestamp() )
426 result = mockResult;
427 }
428 }
429
430 return result;
431 }
432
433 public void setOperation( WsdlOperation operation )
434 {
435 WsdlOperation oldOperation = getOperation();
436
437 if( operation == null )
438 {
439 getConfig().unsetInterface();
440 getConfig().unsetOperation();
441 }
442 else
443 {
444 getConfig().setInterface( operation.getInterface().getName() );
445 getConfig().setOperation( operation.getName() );
446 }
447
448 this.operation = operation;
449
450 notifyPropertyChanged( OPERATION_PROPERTY, oldOperation, operation );
451 }
452
453 @Override
454 public void beforeSave()
455 {
456 for( WsdlMockResponse mockResponse : responses )
457 mockResponse.beforeSave();
458 }
459
460 private class InternalInterfaceListener extends InterfaceListenerAdapter
461 {
462 @Override
463 public void operationUpdated( Operation operation )
464 {
465 if( operation == WsdlMockOperation.this.operation )
466 getConfig().setOperation( operation.getName() );
467 }
468
469 @Override
470 public void operationRemoved( Operation operation )
471 {
472 if( operation == WsdlMockOperation.this.operation )
473 getMockService().removeMockOperation( WsdlMockOperation.this );
474 }
475 }
476
477 private class InternalProjectListener extends ProjectListenerAdapter
478 {
479 @Override
480 public void interfaceRemoved( Interface iface )
481 {
482 if( operation.getInterface() == iface )
483 getMockService().removeMockOperation( WsdlMockOperation.this );
484 }
485 }
486
487 public boolean isOneWay()
488 {
489 return operation == null ? false : operation.isOneWay();
490 }
491
492 public List<? extends ModelItem> getChildren()
493 {
494 return responses;
495 }
496
497 public void onStart()
498 {
499 currentDispatchIndex = 0;
500 }
501 }