View Javadoc

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