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