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