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