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