1
2
3
4
5
6
7
8
9
10
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
252
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
523
524
525
526
527
528
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 }