View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2007 eviware.com 
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of version 2.1 of the GNU Lesser General Public License as published by 
6    *  the Free Software Foundation.
7    *
8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
10   *  See the GNU Lesser General Public License for more details at gnu.org.
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 }