View Javadoc

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