View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2009 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.io.File;
16  import java.io.FileInputStream;
17  import java.io.IOException;
18  import java.io.PrintWriter;
19  import java.io.StringReader;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.LinkedList;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import javax.servlet.http.HttpServletRequest;
29  import javax.servlet.http.HttpServletResponse;
30  import javax.wsdl.Definition;
31  import javax.wsdl.Import;
32  import javax.wsdl.factory.WSDLFactory;
33  import javax.wsdl.xml.WSDLWriter;
34  
35  import org.xml.sax.InputSource;
36  
37  import com.eviware.soapui.SoapUI;
38  import com.eviware.soapui.impl.WsdlInterfaceFactory;
39  import com.eviware.soapui.impl.support.definition.export.WsdlDefinitionExporter;
40  import com.eviware.soapui.impl.wsdl.WsdlInterface;
41  import com.eviware.soapui.impl.wsdl.WsdlOperation;
42  import com.eviware.soapui.impl.wsdl.support.soap.SoapMessageBuilder;
43  import com.eviware.soapui.impl.wsdl.support.soap.SoapUtils;
44  import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
45  import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils;
46  import com.eviware.soapui.impl.wsdl.testcase.WsdlTestRunContext;
47  import com.eviware.soapui.model.iface.Interface;
48  import com.eviware.soapui.model.mock.MockResult;
49  import com.eviware.soapui.model.mock.MockRunListener;
50  import com.eviware.soapui.model.propertyexpansion.PropertyExpander;
51  import com.eviware.soapui.model.support.AbstractMockRunner;
52  import com.eviware.soapui.model.support.ModelSupport;
53  import com.eviware.soapui.monitor.MockEngine;
54  import com.eviware.soapui.support.StringUtils;
55  import com.eviware.soapui.support.Tools;
56  import com.eviware.soapui.support.editor.inspectors.attachments.ContentTypeHandler;
57  import com.eviware.soapui.support.types.StringToStringMap;
58  import com.eviware.soapui.support.xml.XmlUtils;
59  
60  /***
61   * MockRunner that dispatches Http Requests to their designated
62   * WsdlMockOperation if possible
63   * 
64   * @author ole.matzura
65   */
66  
67  public class WsdlMockRunner extends AbstractMockRunner
68  {
69  	private WsdlMockService mockService;
70  	private final List<WsdlMockResult> mockResults = Collections.synchronizedList( new LinkedList<WsdlMockResult>() );
71  	private long maxResults = 100;
72  	private int removed = 0;
73  	private final WsdlMockRunContext mockContext;
74  	private final Map<String, StringToStringMap> wsdlCache = new HashMap<String, StringToStringMap>();
75  	private boolean running;
76  	private boolean logEnabled = true;
77  
78  	public WsdlMockRunner( WsdlMockService mockService, WsdlTestRunContext context ) throws Exception
79  	{
80  		this.mockService = mockService;
81  
82  		Set<WsdlInterface> interfaces = new HashSet<WsdlInterface>();
83  
84  		for( int i = 0; i < mockService.getMockOperationCount(); i++ )
85  		{
86  			WsdlOperation operation = mockService.getMockOperationAt( i ).getOperation();
87  			if( operation != null )
88  				interfaces.add( operation.getInterface() );
89  		}
90  
91  		for( WsdlInterface iface : interfaces )
92  			iface.getWsdlContext().loadIfNecessary();
93  
94  		mockContext = new WsdlMockRunContext( mockService, context );
95  
96  		mockService.runStartScript( mockContext, this );
97  
98  		SoapUI.getMockEngine().startMockService( this );
99  		running = true;
100 
101 		MockRunListener[] mockRunListeners = mockService.getMockRunListeners();
102 
103 		for( MockRunListener listener : mockRunListeners )
104 		{
105 			listener.onMockRunnerStart( this );
106 		}
107 
108 		initWsdlCache();
109 	}
110 
111 	private void initWsdlCache()
112 	{
113 		for( Interface iface : mockService.getMockedInterfaces() )
114 		{
115 			if( !iface.getInterfaceType().equals( WsdlInterfaceFactory.WSDL_TYPE ) )
116 				continue;
117 
118 			try
119 			{
120 				WsdlDefinitionExporter exporter = new WsdlDefinitionExporter( ( WsdlInterface )iface );
121 
122 				String wsdlPrefix = getInterfacePrefix( iface ).substring( 1 );
123 				StringToStringMap parts = exporter.createFilesForExport( "/" + wsdlPrefix + "&part=" );
124 
125 				for( String key : parts.keySet() )
126 				{
127 					if( key.toLowerCase().endsWith( ".wsdl" ) )
128 					{
129 						InputSource inputSource = new InputSource( new StringReader( parts.get( key ) ) );
130 						String content = WsdlUtils.replacePortEndpoint( ( WsdlInterface )iface, inputSource,
131 								getLocalMockServiceEndpoint() );
132 
133 						if( content != null )
134 							parts.put( key, content );
135 					}
136 				}
137 
138 				wsdlCache.put( iface.getName(), parts );
139 
140 				MockEngine.log.info( "Mounted WSDL for interface [" + iface.getName() + "] at [" + getOverviewUrl() + "]" );
141 			}
142 			catch( Exception e )
143 			{
144 				SoapUI.logError( e );
145 			}
146 		}
147 	}
148 
149 	public String getLocalMockServiceEndpoint()
150 	{
151 		String host = mockService.getHost();
152 		if( StringUtils.isNullOrEmpty( host ) )
153 			host = "127.0.0.1";
154 
155 		return "http://" + host + ":" + mockService.getPort() + mockService.getPath();
156 	}
157 
158 	public String getInterfacePrefix( Interface iface )
159 	{
160 		String wsdlPrefix = getOverviewUrl() + "&interface=" + iface.getName();
161 		return wsdlPrefix;
162 	}
163 
164 	public WsdlMockRunContext getMockContext()
165 	{
166 		return mockContext;
167 	}
168 
169 	public synchronized void addMockResult( WsdlMockResult mockResult )
170 	{
171 		if( maxResults > 0 && logEnabled )
172 			mockResults.add( mockResult );
173 
174 		while( mockResults.size() > maxResults )
175 		{
176 			mockResults.remove( 0 );
177 			removed++ ;
178 		}
179 	}
180 
181 	public boolean isRunning()
182 	{
183 		return running;
184 	}
185 
186 	public void stop()
187 	{
188 		if( !isRunning() )
189 			return;
190 
191 		SoapUI.getMockEngine().stopMockService( this );
192 
193 		MockRunListener[] mockRunListeners = mockService.getMockRunListeners();
194 
195 		for( MockRunListener listener : mockRunListeners )
196 		{
197 			listener.onMockRunnerStop( this );
198 		}
199 
200 		try
201 		{
202 			mockService.runStopScript( mockContext, this );
203 			running = false;
204 		}
205 		catch( Exception e )
206 		{
207 			SoapUI.logError( e );
208 		}
209 	}
210 
211 	public WsdlMockService getMockService()
212 	{
213 		return mockService;
214 	}
215 
216 	public long getMaxResults()
217 	{
218 		return maxResults;
219 	}
220 
221 	public synchronized void setMaxResults( long l )
222 	{
223 		this.maxResults = l;
224 
225 		while( mockResults.size() > l )
226 		{
227 			mockResults.remove( 0 );
228 			removed++ ;
229 		}
230 	}
231 
232 	@Override
233 	public MockResult dispatchHeadRequest( HttpServletRequest request, HttpServletResponse response )
234 			throws DispatchException
235 	{
236 		response.setStatus( HttpServletResponse.SC_OK );
237 		return null;
238 	}
239 
240 	@SuppressWarnings( "unchecked" )
241 	public WsdlMockResult dispatchPostRequest( WsdlMockRequest mockRequest ) throws DispatchException
242 	{
243 		WsdlMockResult result = null;
244 
245 		try
246 		{
247 			long timestamp = System.currentTimeMillis();
248 
249 			SoapVersion soapVersion = mockRequest.getSoapVersion();
250 			if( soapVersion == null )
251 				throw new DispatchException( "Unrecognized SOAP Version" );
252 
253 			String soapAction = mockRequest.getSoapAction();
254 			WsdlOperation operation = null;
255 
256 			if( SoapUtils.isSoapFault( mockRequest.getRequestContent(), soapVersion ) )
257 			{
258 				// we should inspect fault detail and try to find matching operation
259 				// but not for now..
260 				WsdlMockOperation faultMockOperation = mockService.getFaultMockOperation();
261 				if( faultMockOperation != null )
262 					operation = faultMockOperation.getOperation();
263 			}
264 			else
265 			{
266 				try
267 				{
268 					operation = SoapUtils.findOperationForRequest( soapVersion, soapAction, mockRequest
269 							.getRequestXmlObject(), mockService.getMockedOperations(), mockService.isRequireSoapVersion(),
270 							mockService.isRequireSoapAction(), mockRequest.getRequestAttachments() );
271 				}
272 				catch( Exception e )
273 				{
274 					if( mockService.isDispatchResponseMessages() )
275 					{
276 						try
277 						{
278 							operation = SoapUtils.findOperationForResponse( soapVersion, soapAction, mockRequest
279 									.getRequestXmlObject(), mockService.getMockedOperations(), mockService
280 									.isRequireSoapVersion(), mockService.isRequireSoapAction() );
281 
282 							if( operation != null )
283 							{
284 								mockRequest.setResponseMessage( true );
285 							}
286 						}
287 						catch( Exception e2 )
288 						{
289 							throw e;
290 						}
291 					}
292 					else
293 					{
294 						throw e;
295 					}
296 				}
297 			}
298 
299 			if( operation != null )
300 			{
301 				WsdlMockOperation mockOperation = mockService.getMockOperation( operation );
302 				if( mockOperation != null )
303 				{
304 					long startTime = System.nanoTime();
305 					try
306 					{
307 						result = mockOperation.dispatchRequest( mockRequest );
308 					}
309 					catch( DispatchException e )
310 					{
311 						result = new WsdlMockResult( mockRequest );
312 
313 						String fault = SoapMessageBuilder.buildFault( "Server", e.getMessage(), mockRequest.getSoapVersion() );
314 						result.setResponseContent( fault );
315 						result.setMockOperation( mockOperation );
316 
317 						mockRequest.getHttpResponse().getWriter().write( fault );
318 					}
319 
320 					if( mockRequest.getHttpRequest() instanceof org.mortbay.jetty.Request )
321 						( ( org.mortbay.jetty.Request )mockRequest.getHttpRequest() ).setHandled( true );
322 
323 					result.setTimeTaken( ( System.nanoTime() - startTime ) / 1000000 );
324 					result.setTimestamp( timestamp );
325 					addMockResult( result );
326 					return result;
327 				}
328 				else
329 				{
330 					throw new DispatchException( "Failed to find matching operation for request" );
331 				}
332 			}
333 
334 			throw new DispatchException( "Missing operation for soapAction [" + soapAction + "] and body element ["
335 					+ XmlUtils.getQName( mockRequest.getContentElement() ) + "] with SOAP Version ["
336 					+ mockRequest.getSoapVersion() + "]" );
337 		}
338 		catch( Exception e )
339 		{
340 			if( e instanceof DispatchException )
341 				throw ( DispatchException )e;
342 
343 			throw new DispatchException( e );
344 		}
345 	}
346 
347 	public MockResult getMockResultAt( int index )
348 	{
349 		return index <= removed ? null : mockResults.get( index - removed );
350 	}
351 
352 	public int getMockResultCount()
353 	{
354 		return mockResults.size() + removed;
355 	}
356 
357 	public synchronized void clearResults()
358 	{
359 		mockResults.clear();
360 	}
361 
362 	public void release()
363 	{
364 		clearResults();
365 		mockService = null;
366 		mockContext.clear();
367 	}
368 
369 	@Override
370 	public MockResult dispatchRequest( HttpServletRequest request, HttpServletResponse response )
371 			throws DispatchException
372 	{
373 		Object result = null;
374 
375 		try
376 		{
377 			for( MockRunListener listener : mockService.getMockRunListeners() )
378 			{
379 				result = listener.onMockRequest( this, request, response );
380 				if( result instanceof MockResult )
381 					return ( MockResult )result;
382 			}
383 
384 			WsdlMockRequest mockRequest = new WsdlMockRequest( request, response, mockContext );
385 			result = mockService.runOnRequestScript( mockContext, this, mockRequest );
386 			if( !( result instanceof MockResult ) )
387 			{
388 				String method = request.getMethod();
389 
390 				if( method.equals( "POST" ) )
391 					result = dispatchPostRequest( mockRequest );
392 				else
393 					result = super.dispatchRequest( request, response );
394 			}
395 
396 			mockService.runAfterRequestScript( mockContext, this, ( MockResult )result );
397 			return ( MockResult )result;
398 		}
399 		catch( Exception e )
400 		{
401 			if( e instanceof DispatchException )
402 				throw (DispatchException)e;
403 			else
404 				throw new DispatchException( e );
405 		}
406 		finally
407 		{
408 			if( result instanceof MockResult )
409 			{
410 				for( MockRunListener listener : mockService.getMockRunListeners() )
411 				{
412 					listener.onMockResult( ( MockResult )result );
413 				}
414 			}
415 		}
416 	}
417 
418 	public MockResult dispatchGetRequest( HttpServletRequest request, HttpServletResponse response )
419 			throws DispatchException
420 	{
421 		try
422 		{
423 			if( request.getQueryString() != null && request.getQueryString().toUpperCase().startsWith( "WSDL" ) )
424 			{
425 				dispatchWsdlRequest( request, response );
426 			}
427 			else
428 			{
429 				String docroot = PropertyExpander.expandProperties( mockContext, getMockService().getDocroot() );
430 				if( StringUtils.hasContent( docroot ) )
431 				{
432 					try
433 					{
434 						String pathInfo = request.getPathInfo();
435 						if( mockService.getPath().length() > 1 && pathInfo.startsWith( mockService.getPath() ) )
436 							pathInfo = pathInfo.substring( mockService.getPath().length() );
437 
438 						File file = new File( docroot + pathInfo.replace( '/', File.separatorChar ) );
439 						FileInputStream in = new FileInputStream( file );
440 						response.setStatus( HttpServletResponse.SC_OK );
441 						long length = file.length();
442 						response.setContentLength( ( int )length );
443 						response.setContentType( ContentTypeHandler.getContentTypeFromFilename( file.getName() ) );
444 						Tools.readAndWrite( in, length, response.getOutputStream() );
445 					}
446 					catch( Throwable e )
447 					{
448 						throw new DispatchException( e );
449 					}
450 				}
451 			}
452 
453 			return null;
454 		}
455 		catch( Exception e )
456 		{
457 			throw new DispatchException( e );
458 		}
459 	}
460 
461 	protected void dispatchWsdlRequest( HttpServletRequest request, HttpServletResponse response ) throws IOException
462 	{
463 		if( request.getQueryString().equalsIgnoreCase( "WSDL" ) )
464 		{
465 			printWsdl( response );
466 			return;
467 		}
468 
469 		String ifaceName = request.getParameter( "interface" );
470 		WsdlInterface iface = ( WsdlInterface )mockService.getProject().getInterfaceByName( ifaceName );
471 		if( iface == null )
472 		{
473 			printInterfaceList( response );
474 			return;
475 		}
476 
477 		StringToStringMap parts = wsdlCache.get( iface.getName() );
478 		String part = request.getParameter( "part" );
479 		String content = StringUtils.isNullOrEmpty( part ) ? null : parts.get( part );
480 
481 		if( content == null )
482 		{
483 			printPartList( iface, parts, response );
484 			return;
485 		}
486 
487 		if( content != null )
488 		{
489 			printOkXmlResult( response, content );
490 		}
491 	}
492 
493 	private void printOkXmlResult( HttpServletResponse response, String content ) throws IOException
494 	{
495 		response.setStatus( HttpServletResponse.SC_OK );
496 		response.setContentType( "text/xml" );
497 		response.setCharacterEncoding( "UTF-8" );
498 		response.getWriter().print( content );
499 	}
500 
501 	private void printWsdl( HttpServletResponse response ) throws IOException
502 	{
503 		WsdlInterface[] mockedInterfaces = mockService.getMockedInterfaces();
504 		if( mockedInterfaces.length == 1 )
505 		{
506 			StringToStringMap parts = wsdlCache.get( mockedInterfaces[0].getName() );
507 			printOkXmlResult( response, parts.get( parts.get( "#root#" ) ) );
508 		}
509 		else
510 		{
511 			try
512 			{
513 				WSDLFactory wsdlFactory = WSDLFactory.newInstance();
514 				Definition def = wsdlFactory.newDefinition();
515 				for( WsdlInterface iface : mockedInterfaces )
516 				{
517 					StringToStringMap parts = wsdlCache.get( iface.getName() );
518 					Import wsdlImport = def.createImport();
519 					wsdlImport.setLocationURI( getInterfacePrefix( iface ) + "&part=" + parts.get( "#root#" ) );
520 					wsdlImport.setNamespaceURI( iface.getWsdlContext().getDefinition().getTargetNamespace() );
521 
522 					def.addImport( wsdlImport );
523 				}
524 
525 				response.setStatus( HttpServletResponse.SC_OK );
526 				response.setContentType( "text/xml" );
527 				response.setCharacterEncoding( "UTF-8" );
528 
529 				WSDLWriter writer = wsdlFactory.newWSDLWriter();
530 				writer.writeWSDL( def, response.getWriter() );
531 			}
532 			catch( Exception e )
533 			{
534 				SoapUI.logError( e );
535 				throw new IOException( "Failed to create combined WSDL" );
536 			}
537 		}
538 	}
539 
540 	private void printPartList( WsdlInterface iface, StringToStringMap parts, HttpServletResponse response )
541 			throws IOException
542 	{
543 		response.setStatus( HttpServletResponse.SC_OK );
544 		response.setContentType( "text/html" );
545 
546 		PrintWriter out = response.getWriter();
547 		out.print( "<html><body><p>Parts in interface [" + iface.getName() + "]</p><ul>" );
548 
549 		for( String key : parts.keySet() )
550 		{
551 			if( key.equals( "#root#" ) )
552 				continue;
553 
554 			out.print( "<li><a href=\"" );
555 			out.print( getInterfacePrefix( iface ) + "&part=" + key );
556 			out.print( "\">" + key + "</a></li>" );
557 		}
558 
559 		out.print( "</ul></p></body></html>" );
560 	}
561 
562 	private void printInterfaceList( HttpServletResponse response ) throws IOException
563 	{
564 		response.setStatus( HttpServletResponse.SC_OK );
565 		response.setContentType( "text/html" );
566 
567 		PrintWriter out = response.getWriter();
568 		out.print( "<html><body><p>Mocked Interfaces in project [" + mockService.getProject().getName() + "]</p><ul>" );
569 
570 		for( Interface iface : ModelSupport.getChildren( mockService.getProject(), WsdlInterface.class ) )
571 		{
572 			out.print( "<li><a href=\"" );
573 			out.print( getInterfacePrefix( iface ) );
574 			out.print( "\">" + iface.getName() + "</a></li>" );
575 		}
576 
577 		out.print( "</ul></p></body></html>" );
578 	}
579 
580 	public String getOverviewUrl()
581 	{
582 		return mockService.getPath() + "?WSDL";
583 	}
584 
585 	public void setLogEnabled( boolean logEnabled )
586 	{
587 		this.logEnabled = logEnabled;
588 	}
589 }