/*
* soapUI, copyright (C) 2004-2011 eviware.com
*
* soapUI is free software; you can redistribute it and/or modify it under the
* terms of version 2.1 of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details at gnu.org.
*/
package com.eviware.soapui.impl.wsdl.mock;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.wsdl.Definition;
import javax.wsdl.Import;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLWriter;
import org.apache.commons.collections.list.TreeList;
import org.apache.log4j.Logger;
import org.xml.sax.InputSource;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.WsdlInterfaceFactory;
import com.eviware.soapui.impl.support.definition.export.WsdlDefinitionExporter;
import com.eviware.soapui.impl.wsdl.WsdlInterface;
import com.eviware.soapui.impl.wsdl.WsdlOperation;
import com.eviware.soapui.impl.wsdl.support.soap.SoapUtils;
import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils;
import com.eviware.soapui.impl.wsdl.testcase.WsdlTestRunContext;
import com.eviware.soapui.model.iface.Interface;
import com.eviware.soapui.model.mock.MockResult;
import com.eviware.soapui.model.mock.MockRunListener;
import com.eviware.soapui.model.propertyexpansion.PropertyExpander;
import com.eviware.soapui.model.support.AbstractMockRunner;
import com.eviware.soapui.model.support.ModelSupport;
import com.eviware.soapui.support.StringUtils;
import com.eviware.soapui.support.Tools;
import com.eviware.soapui.support.editor.inspectors.attachments.ContentTypeHandler;
import com.eviware.soapui.support.types.StringToStringMap;
import com.eviware.soapui.support.xml.XmlUtils;
/**
* MockRunner that dispatches Http Requests to their designated
* WsdlMockOperation if possible
*
* @author ole.matzura
*/
@SuppressWarnings( "unchecked" )
public class WsdlMockRunner extends AbstractMockRunner
{
private WsdlMockService mockService;
private final List<WsdlMockResult> mockResults = Collections.synchronizedList( new TreeList() );
private long maxResults = 100;
private int removed = 0;
private final WsdlMockRunContext mockContext;
private final Map<String, StringToStringMap> wsdlCache = new HashMap<String, StringToStringMap>();
private boolean running;
private boolean logEnabled = true;
private final static Logger log = Logger.getLogger( WsdlMockRunner.class );
public WsdlMockRunner( WsdlMockService mockService, WsdlTestRunContext context ) throws Exception
{
this.mockService = mockService;
Set<WsdlInterface> interfaces = new HashSet<WsdlInterface>();
for( int i = 0; i < mockService.getMockOperationCount(); i++ )
{
WsdlOperation operation = mockService.getMockOperationAt( i ).getOperation();
if( operation != null )
interfaces.add( operation.getInterface() );
}
for( WsdlInterface iface : interfaces )
iface.getWsdlContext().loadIfNecessary();
initWsdlCache();
mockContext = new WsdlMockRunContext( mockService, context );
start();
}
private void initWsdlCache()
{
for( Interface iface : mockService.getMockedInterfaces() )
{
if( !iface.getInterfaceType().equals( WsdlInterfaceFactory.WSDL_TYPE ) )
continue;
try
{
WsdlDefinitionExporter exporter = new WsdlDefinitionExporter( ( WsdlInterface )iface );
String wsdlPrefix = getInterfacePrefix( iface ).substring( 1 );
StringToStringMap parts = exporter.createFilesForExport( "/" + wsdlPrefix + "&part=" );
for( String key : parts.keySet() )
{
if( key.toLowerCase().endsWith( ".wsdl" ) )
{
InputSource inputSource = new InputSource( new StringReader( parts.get( key ) ) );
String content = WsdlUtils.replacePortEndpoint( ( WsdlInterface )iface, inputSource,
mockService.getLocalMockServiceEndpoint() );
if( content != null )
parts.put( key, content );
}
}
wsdlCache.put( iface.getName(), parts );
log.info( "Mounted WSDL for interface [" + iface.getName() + "] at [" + getOverviewUrl() + "]" );
}
catch( Exception e )
{
SoapUI.logError( e );
}
}
}
public String getInterfacePrefix( Interface iface )
{
String wsdlPrefix = getOverviewUrl() + "&interface=" + iface.getName();
return wsdlPrefix;
}
public WsdlMockRunContext getMockContext()
{
return mockContext;
}
public synchronized void addMockResult( WsdlMockResult mockResult )
{
if( maxResults > 0 && logEnabled )
mockResults.add( mockResult );
while( mockResults.size() > maxResults )
{
mockResults.remove( 0 );
removed++ ;
}
}
public boolean isRunning()
{
return running;
}
public void stop()
{
if( !isRunning() )
return;
SoapUI.getMockEngine().stopMockService( this );
MockRunListener[] mockRunListeners = mockService.getMockRunListeners();
for( MockRunListener listener : mockRunListeners )
{
listener.onMockRunnerStop( this );
}
try
{
mockService.runStopScript( mockContext, this );
running = false;
}
catch( Exception e )
{
SoapUI.logError( e );
}
}
public WsdlMockService getMockService()
{
return mockService;
}
public long getMaxResults()
{
return maxResults;
}
public synchronized void setMaxResults( long l )
{
this.maxResults = l;
while( mockResults.size() > l )
{
mockResults.remove( 0 );
removed++ ;
}
}
@Override
public MockResult dispatchHeadRequest( HttpServletRequest request, HttpServletResponse response )
throws DispatchException
{
response.setStatus( HttpServletResponse.SC_OK );
return null;
}
public WsdlMockResult dispatchPostRequest( WsdlMockRequest mockRequest ) throws Exception
{
WsdlMockResult result = null;
try
{
long timestamp = System.currentTimeMillis();
SoapVersion soapVersion = mockRequest.getSoapVersion();
if( soapVersion == null )
throw new DispatchException( "Unrecognized SOAP Version" );
String soapAction = mockRequest.getSoapAction();
WsdlOperation operation = null;
if( SoapUtils.isSoapFault( mockRequest.getRequestContent(), soapVersion ) )
{
// we should inspect fault detail and try to find matching operation
// but not for now..
WsdlMockOperation faultMockOperation = mockService.getFaultMockOperation();
if( faultMockOperation != null )
operation = faultMockOperation.getOperation();
}
else
{
try
{
operation = SoapUtils.findOperationForRequest( soapVersion, soapAction,
mockRequest.getRequestXmlObject(), mockService.getMockedOperations(),
mockService.isRequireSoapVersion(), mockService.isRequireSoapAction(),
mockRequest.getRequestAttachments() );
}
catch( Exception e )
{
if( mockService.isDispatchResponseMessages() )
{
try
{
operation = SoapUtils.findOperationForResponse( soapVersion, soapAction,
mockRequest.getRequestXmlObject(), mockService.getMockedOperations(),
mockService.isRequireSoapVersion(), mockService.isRequireSoapAction() );
if( operation != null )
{
mockRequest.setResponseMessage( true );
}
}
catch( Exception e2 )
{
throw e;
}
}
else
{
throw e;
}
}
}
if( operation != null )
{
WsdlMockOperation mockOperation = mockService.getMockOperation( operation );
if( mockOperation != null )
{
long startTime = System.nanoTime();
// try
// {
result = mockOperation.dispatchRequest( mockRequest );
// }
// catch( DispatchException e )
// {
// result = new WsdlMockResult( mockRequest );
//
// String fault = SoapMessageBuilder.buildFault( "Server",
// e.getMessage(), mockRequest.getSoapVersion() );
// result.setResponseContent( fault );
// result.setMockOperation( mockOperation );
//
// mockRequest.getHttpResponse().getWriter().write( fault );
// }
if( mockRequest.getHttpRequest() instanceof org.mortbay.jetty.Request )
( ( org.mortbay.jetty.Request )mockRequest.getHttpRequest() ).setHandled( true );
result.setTimeTaken( ( System.nanoTime() - startTime ) / 1000000 );
result.setTimestamp( timestamp );
addMockResult( result );
return result;
}
else
{
throw new DispatchException( "Failed to find matching operation for request" );
}
}
throw new DispatchException( "Missing operation for soapAction [" + soapAction + "] and body element ["
+ XmlUtils.getQName( mockRequest.getContentElement() ) + "] with SOAP Version ["
+ mockRequest.getSoapVersion() + "]" );
}
catch( Exception e )
{
if( e instanceof DispatchException )
throw ( DispatchException )e;
throw new DispatchException( e );
}
}
public MockResult getMockResultAt( int index )
{
return index <= removed ? null : mockResults.get( index - removed );
}
public int getMockResultCount()
{
return mockResults.size() + removed;
}
public synchronized void clearResults()
{
mockResults.clear();
}
public void release()
{
clearResults();
mockService = null;
mockContext.clear();
}
@Override
public MockResult dispatchRequest( HttpServletRequest request, HttpServletResponse response )
throws DispatchException
{
Object result = null;
try
{
for( MockRunListener listener : mockService.getMockRunListeners() )
{
result = listener.onMockRequest( this, request, response );
if( result instanceof MockResult )
return ( MockResult )result;
}
WsdlMockRequest mockRequest = new WsdlMockRequest( request, response, mockContext );
result = mockService.runOnRequestScript( mockContext, this, mockRequest );
if( !( result instanceof MockResult ) )
{
String method = mockRequest.getMethod();
if( method.equals( "POST" ) )
result = dispatchPostRequest( mockRequest );
else
result = super.dispatchRequest( request, response );
}
mockService.runAfterRequestScript( mockContext, this, ( MockResult )result );
return ( MockResult )result;
}
catch( Throwable e )
{
if( e instanceof DispatchException )
throw ( DispatchException )e;
else
throw new DispatchException( e );
}
finally
{
if( result instanceof MockResult )
{
for( MockRunListener listener : mockService.getMockRunListeners() )
{
listener.onMockResult( ( MockResult )result );
}
}
}
}
public MockResult dispatchGetRequest( HttpServletRequest request, HttpServletResponse response )
throws DispatchException
{
try
{
String qs = request.getQueryString();
if( qs != null && qs.toUpperCase().startsWith( "WSDL" ) )
{
dispatchWsdlRequest( request, response );
}
else
{
if( qs != null && qs.startsWith( "cmd=" ) )
{
dispatchCommand( request.getParameter( "cmd" ), request, response );
}
else
{
String docroot = PropertyExpander.expandProperties( mockContext, getMockService().getDocroot() );
if( StringUtils.hasContent( docroot ) )
{
try
{
String pathInfo = request.getPathInfo();
if( pathInfo == null )
pathInfo = "";
if( mockService.getPath().length() > 1 && pathInfo.startsWith( mockService.getPath() ) )
pathInfo = pathInfo.substring( mockService.getPath().length() );
String filename = docroot + pathInfo.replace( '/', File.separatorChar );
File file = new File( filename );
if( file.exists() )
{
returnFile( response, file );
}
}
catch( Throwable e )
{
throw new DispatchException( e );
}
}
}
}
return null;
}
catch( Exception e )
{
throw new DispatchException( e );
}
}
public void returnFile( HttpServletResponse response, File file ) throws FileNotFoundException, IOException
{
FileInputStream in = new FileInputStream( file );
response.setStatus( HttpServletResponse.SC_OK );
long length = file.length();
response.setContentLength( ( int )length );
response.setContentType( ContentTypeHandler.getContentTypeFromFilename( file.getName() ) );
Tools.readAndWrite( in, length, response.getOutputStream() );
in.close();
}
private void dispatchCommand( String cmd, HttpServletRequest request, HttpServletResponse response )
throws IOException
{
if( "stop".equals( cmd ) )
{
response.setStatus( HttpServletResponse.SC_OK );
response.flushBuffer();
SoapUI.getThreadPool().execute( new Runnable()
{
public void run()
{
try
{
Thread.sleep( 500 );
}
catch( InterruptedException e )
{
e.printStackTrace();
}
stop();
}
} );
}
else if( "restart".equals( cmd ) )
{
response.setStatus( HttpServletResponse.SC_OK );
response.flushBuffer();
SoapUI.getThreadPool().execute( new Runnable()
{
public void run()
{
try
{
Thread.sleep( 500 );
}
catch( InterruptedException e )
{
e.printStackTrace();
}
stop();
//
// try
// {
// Thread.sleep( 500 );
// }
// catch( InterruptedException e )
// {
// e.printStackTrace();
// }
try
{
mockService.start();
}
catch( Exception e )
{
e.printStackTrace();
}
}
} );
}
}
protected void dispatchWsdlRequest( HttpServletRequest request, HttpServletResponse response ) throws IOException
{
if( request.getQueryString().equalsIgnoreCase( "WSDL" ) )
{
printWsdl( response );
return;
}
String ifaceName = request.getParameter( "interface" );
WsdlInterface iface = ( WsdlInterface )mockService.getProject().getInterfaceByName( ifaceName );
if( iface == null )
{
printInterfaceList( response );
return;
}
StringToStringMap parts = wsdlCache.get( iface.getName() );
String part = request.getParameter( "part" );
String content = StringUtils.isNullOrEmpty( part ) ? null : parts.get( part );
if( content == null )
{
printPartList( iface, parts, response );
return;
}
if( content != null )
{
printOkXmlResult( response, content );
}
}
public void printOkXmlResult( HttpServletResponse response, String content ) throws IOException
{
response.setStatus( HttpServletResponse.SC_OK );
response.setContentType( "text/xml" );
response.setCharacterEncoding( "UTF-8" );
response.getWriter().print( content );
}
public void printWsdl( HttpServletResponse response ) throws IOException
{
WsdlInterface[] mockedInterfaces = mockService.getMockedInterfaces();
if( mockedInterfaces.length == 1 )
{
StringToStringMap parts = wsdlCache.get( mockedInterfaces[0].getName() );
printOkXmlResult( response, parts.get( parts.get( "#root#" ) ) );
}
else
{
try
{
WSDLFactory wsdlFactory = WSDLFactory.newInstance();
Definition def = wsdlFactory.newDefinition();
for( WsdlInterface iface : mockedInterfaces )
{
StringToStringMap parts = wsdlCache.get( iface.getName() );
Import wsdlImport = def.createImport();
wsdlImport.setLocationURI( getInterfacePrefix( iface ) + "&part=" + parts.get( "#root#" ) );
wsdlImport.setNamespaceURI( WsdlUtils.getTargetNamespace( iface.getWsdlContext().getDefinition() ) );
def.addImport( wsdlImport );
}
response.setStatus( HttpServletResponse.SC_OK );
response.setContentType( "text/xml" );
response.setCharacterEncoding( "UTF-8" );
WSDLWriter writer = wsdlFactory.newWSDLWriter();
writer.writeWSDL( def, response.getWriter() );
}
catch( Exception e )
{
SoapUI.logError( e );
throw new IOException( "Failed to create combined WSDL" );
}
}
}
public void printPartList( WsdlInterface iface, StringToStringMap parts, HttpServletResponse response )
throws IOException
{
response.setStatus( HttpServletResponse.SC_OK );
response.setContentType( "text/html" );
PrintWriter out = response.getWriter();
out.print( "<html><body><p>Parts in interface [" + iface.getName() + "]</p><ul>" );
for( String key : parts.keySet() )
{
if( key.equals( "#root#" ) )
continue;
out.print( "<li><a href=\"" );
out.print( getInterfacePrefix( iface ) + "&part=" + key );
out.print( "\">" + key + "</a></li>" );
}
out.print( "</ul></p></body></html>" );
}
public void printInterfaceList( HttpServletResponse response ) throws IOException
{
response.setStatus( HttpServletResponse.SC_OK );
response.setContentType( "text/html" );
PrintWriter out = response.getWriter();
out.print( "<html><body><p>Mocked Interfaces in project [" + mockService.getProject().getName() + "]</p><ul>" );
for( Interface iface : ModelSupport.getChildren( mockService.getProject(), WsdlInterface.class ) )
{
out.print( "<li><a href=\"" );
out.print( getInterfacePrefix( iface ) );
out.print( "\">" + iface.getName() + "</a></li>" );
}
out.print( "</ul></p></body></html>" );
}
public String getOverviewUrl()
{
return mockService.getPath() + "?WSDL";
}
public void setLogEnabled( boolean logEnabled )
{
this.logEnabled = logEnabled;
}
public void start() throws Exception
{
if( running )
return;
mockContext.reset();
mockService.runStartScript( mockContext, this );
SoapUI.getMockEngine().startMockService( this );
running = true;
MockRunListener[] mockRunListeners = mockService.getMockRunListeners();
for( MockRunListener listener : mockRunListeners )
{
listener.onMockRunnerStart( this );
}
}
}