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.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 }