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 interfaceListener = new InternalInterfaceListener();
130 }
131
132 @Override
133 public ImageIcon getIcon()
134 {
135 if( operation != null && operation.isOneWay() )
136 return oneWayIcon;
137 else
138 return super.getIcon();
139 }
140
141 public WsdlMockService getMockService()
142 {
143 return ( WsdlMockService ) getParent();
144 }
145
146 public WsdlMockResponse getMockResponseAt( int index )
147 {
148 return responses.get( index );
149 }
150
151 public WsdlOperation getOperation()
152 {
153 return operation;
154 }
155
156 public WsdlMockResponse getMockResponseByName( String name )
157 {
158 return ( WsdlMockResponse ) getWsdlModelItemByName( responses, name );
159 }
160
161 public int getMockResponseCount()
162 {
163 return responses.size();
164 }
165
166 public WsdlMockResponse addNewMockResponse( MockResponseConfig responseConfig )
167 {
168 WsdlMockResponse mockResponse = new WsdlMockResponse( this, responseConfig );
169
170 responses.add( mockResponse );
171 if( responses.size() == 1 )
172 setDefaultResponse( mockResponse.getName() );
173
174 (getMockService()).fireMockResponseAdded( mockResponse );
175
176 return mockResponse;
177 }
178
179 public WsdlMockResponse addNewMockResponse( String name, boolean createResponse )
180 {
181 MockResponseConfig responseConfig = getConfig().addNewResponse();
182 responseConfig.setName( name );
183 responseConfig.addNewResponseContent();
184
185 if( createResponse && getOperation() != null && !getOperation().isOneWay())
186 {
187 boolean createOptional = SoapUI.getSettings().getBoolean( WsdlSettings.XML_GENERATION_ALWAYS_INCLUDE_OPTIONAL_ELEMENTS );
188 CompressedStringSupport.setString( responseConfig.getResponseContent(), getOperation().createResponse( createOptional ));
189 }
190
191 return addNewMockResponse( responseConfig );
192 }
193
194 public void removeMockResponse( WsdlMockResponse mockResponse )
195 {
196 int ix = responses.indexOf( mockResponse );
197 responses.remove( ix );
198 mockResponse.removePropertyChangeListener( this );
199
200 try
201 {
202 (getMockService()).fireMockResponseRemoved( mockResponse );
203 }
204 finally
205 {
206 mockResponse.release();
207 getConfig().removeResponse( ix );
208 }
209 }
210
211 public WsdlMockResult dispatchRequest( WsdlMockRequest request, HttpServletResponse response ) throws DispatchException
212 {
213 try
214 {
215 request.setOperation( getOperation() );
216 WsdlMockResult result = new WsdlMockResult( request, response );
217
218 if( getMockResponseCount() == 0 )
219 throw new DispatchException( "Missing MockResponse(s) in MockOperation [" + getName() + "]" );
220
221 if( getDispatchStyle() == DispatchStyleConfig.XPATH )
222 {
223 XmlObject[] items = evaluateDispatchXPath( request );
224 for( XmlObject item : items )
225 {
226 WsdlMockResponse mockResponse = getMockResponseByName( XmlUtils.getNodeValue( item.getDomNode() ));
227
228 if( mockResponse == null )
229 mockResponse = getMockResponseByName( getDefaultResponse() );
230
231 if( mockResponse != null )
232 {
233 result.setMockResponse( mockResponse );
234 mockResponse.execute( request, result );
235
236 return result;
237 }
238 }
239
240 throw new DispatchException( "Missing matching response message" );
241 }
242 else if( getDispatchStyle() == DispatchStyleConfig.SCRIPT )
243 {
244 Object retVal = evaluateDispatchScript( request );
245
246 WsdlMockResponse mockResponse = retVal == null ? getMockResponseByName( getDefaultResponse() )
247 : getMockResponseByName( retVal.toString() );
248
249 if( mockResponse != null )
250 {
251 result.setMockResponse( mockResponse );
252 mockResponse.execute( request, result );
253
254 return result;
255 }
256 else
257 {
258 throw new DispatchException( "Missing matching response message [" + retVal + "]" );
259 }
260 }
261 else
262 {
263 WsdlMockResponse mockResponse = null;
264 synchronized( this )
265 {
266 if( getDispatchStyle() == DispatchStyleConfig.RANDOM )
267 {
268 currentDispatchIndex = ( int ) ( (Math.random() * getMockResponseCount()) + 0.5F );
269 }
270
271 if( currentDispatchIndex >= getMockResponseCount() )
272 currentDispatchIndex = 0;
273
274 mockResponse = getMockResponseAt( currentDispatchIndex );
275 result.setMockResponse( mockResponse );
276
277 currentDispatchIndex++;
278 }
279
280 mockResponse.execute( request, result );
281 }
282
283 return result;
284 }
285 catch( Exception e )
286 {
287 throw new DispatchException( e );
288 }
289 }
290
291 @Override
292 public void release()
293 {
294 super.release();
295
296 for( WsdlMockResponse response : responses )
297 {
298 response.removePropertyChangeListener( this );
299 response.release();
300 }
301
302 scriptEnginePool.release();
303
304 operation.getInterface().getProject().removeProjectListener( projectListener );
305 operation.getInterface().removeInterfaceListener( interfaceListener );
306 operation.getInterface().removePropertyChangeListener( WsdlInterface.NAME_PROPERTY, this );
307 }
308
309 public XmlObject[] evaluateDispatchXPath( WsdlMockRequest request ) throws XmlException
310 {
311 XmlObject xmlObject = request.getRequestXmlObject();
312 XmlObject[] items = xmlObject.selectPath( getDispatchPath() );
313 return items;
314 }
315
316 public Object evaluateDispatchScript( WsdlMockRequest request ) throws DispatchException
317 {
318 String dispatchPath = getDispatchPath();
319 if( dispatchPath == null || dispatchPath.trim().length() == 0 )
320 {
321 throw new DispatchException( "Dispatch Script is empty" );
322 }
323
324 SoapUIScriptEngine scriptEngine = scriptEnginePool.getScriptEngine();
325
326 try
327 {
328 WsdlMockService mockService = getMockService();
329 WsdlMockRunner mockRunner = mockService.getMockRunner();
330 MockRunContext context = mockRunner == null ? new WsdlMockRunContext( mockService, null ) : mockRunner.getMockContext();
331
332 scriptEngine.setVariable( "context", context );
333 scriptEngine.setVariable( "requestContext", request.getRequestContext() );
334 scriptEngine.setVariable( "mockRequest", request);
335 scriptEngine.setVariable( "mockOperation", this );
336 scriptEngine.setVariable( "log", SoapUI.ensureGroovyLog() );
337
338 scriptEngine.setScript( dispatchPath );
339 Object retVal = scriptEngine.run();
340 return retVal;
341 }
342 catch( Throwable e )
343 {
344 SoapUI.logError( e );
345 throw new DispatchException( "Failed to dispatch using script; " + e );
346 }
347 finally
348 {
349 scriptEnginePool.returnScriptEngine( scriptEngine );
350 }
351 }
352
353 public DispatchStyleConfig.Enum getDispatchStyle()
354 {
355 return getConfig().isSetDispatchStyle() ? getConfig().getDispatchStyle() : DispatchStyleConfig.SEQUENCE;
356 }
357
358 public void setDispatchStyle( DispatchStyleConfig.Enum dispatchStyle )
359 {
360 Enum old = getDispatchStyle();
361 getConfig().setDispatchStyle( dispatchStyle );
362 notifyPropertyChanged( DISPATCH_STYLE_PROPERTY, old, dispatchStyle );
363 }
364
365 public String getDispatchPath()
366 {
367 return getConfig().getDispatchPath();
368 }
369
370 public void setDispatchPath( String dispatchPath )
371 {
372 String old = getDispatchPath();
373 getConfig().setDispatchPath( dispatchPath );
374 notifyPropertyChanged( DISPATCH_PATH_PROPERTY, old, dispatchPath );
375
376 scriptEnginePool.setScript( dispatchPath );
377 }
378
379 public String getWsdlOperationName()
380 {
381 return operation == null ? null : operation.getName();
382 }
383
384 public String getDefaultResponse()
385 {
386 return getConfig().getDefaultResponse();
387 }
388
389 public void setDefaultResponse( String defaultResponse )
390 {
391 String old = getDefaultResponse();
392 getConfig().setDefaultResponse( defaultResponse );
393 notifyPropertyChanged( DEFAULT_RESPONSE_PROPERTY, old, defaultResponse );
394 }
395
396 public List<MockResponse> getMockResponses()
397 {
398 return new ArrayList<MockResponse>( responses );
399 }
400
401 public void propertyChange( PropertyChangeEvent arg0 )
402 {
403 if( arg0.getPropertyName().equals( WsdlMockResponse.NAME_PROPERTY ))
404 {
405 if( arg0.getOldValue().equals( getDefaultResponse() ))
406 setDefaultResponse( arg0.getNewValue().toString() );
407 }
408 else if( arg0.getPropertyName().equals( WsdlInterface.NAME_PROPERTY ))
409 {
410 getConfig().setInterface( arg0.getNewValue().toString() );
411 }
412 }
413
414 public WsdlMockResult getLastMockResult()
415 {
416 WsdlMockResult result = null;
417
418 for( WsdlMockResponse response : responses )
419 {
420 WsdlMockResult mockResult = response.getMockResult();
421 if( mockResult != null )
422 {
423 if( result == null || result.getTimestamp() > mockResult.getTimestamp() )
424 result = mockResult;
425 }
426 }
427
428 return result;
429 }
430
431 public void setOperation( WsdlOperation operation )
432 {
433 WsdlOperation oldOperation = getOperation();
434
435 if( operation == null )
436 {
437 getConfig().unsetInterface();
438 getConfig().unsetOperation();
439 }
440 else
441 {
442 getConfig().setInterface( operation.getInterface().getName() );
443 getConfig().setOperation( operation.getName() );
444 }
445
446 this.operation = operation;
447
448 notifyPropertyChanged( OPERATION_PROPERTY, oldOperation, operation );
449 }
450
451 @Override
452 public void beforeSave()
453 {
454 for( WsdlMockResponse mockResponse : responses )
455 mockResponse.beforeSave();
456 }
457
458 private class InternalInterfaceListener extends InterfaceListenerAdapter
459 {
460 @Override
461 public void operationUpdated( Operation operation )
462 {
463 if( operation == WsdlMockOperation.this.operation )
464 getConfig().setOperation( operation.getName() );
465 }
466
467 @Override
468 public void operationRemoved( Operation operation )
469 {
470 if( operation == WsdlMockOperation.this.operation )
471 getMockService().removeMockOperation( WsdlMockOperation.this );
472 }
473 }
474
475 private class InternalProjectListener extends ProjectListenerAdapter
476 {
477 @Override
478 public void interfaceRemoved( Interface iface )
479 {
480 if( operation.getInterface() == iface )
481 getMockService().removeMockOperation( WsdlMockOperation.this );
482 }
483 }
484
485 public boolean isOneWay()
486 {
487 return operation == null ? false : operation.isOneWay();
488 }
489
490 public List<? extends ModelItem> getChildren()
491 {
492 return responses;
493 }
494 }