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 }