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.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             // we should inspect fault detail and try to find matching operation but not for now..
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 }