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