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