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          WsdlOperation operation = null;
247 
248          if( SoapUtils.isSoapFault( mockRequest.getRequestContent(), soapVersion ) )
249          {
250             // we should inspect fault detail and try to find matching operation but not for now..
251             WsdlMockOperation faultMockOperation = mockService.getFaultMockOperation();
252             if( faultMockOperation != null )
253                operation = faultMockOperation.getOperation();
254          }
255          else
256          {
257             try
258             {
259                operation = SoapUtils.findOperationForRequest( soapVersion, soapAction, mockRequest
260                        .getRequestXmlObject(), mockService.getMockedOperations(),
261                        mockService.isRequireSoapVersion(), mockService.isRequireSoapAction() );
262             }
263             catch( Exception e )
264             {
265                if( mockService.isDispatchResponseMessages() )
266                {
267                   try
268                   {
269                      operation = SoapUtils.findOperationForResponse( soapVersion, soapAction, mockRequest
270                              .getRequestXmlObject(), mockService.getMockedOperations(),
271                              mockService.isRequireSoapVersion(), mockService.isRequireSoapAction() );
272 
273                      if( operation != null )
274                      {
275                         mockRequest.setResponseMessage( true );
276                      }
277                   }
278                   catch( Exception e2 )
279                   {
280                      throw e;
281                   }
282                }
283                else
284                {
285                   throw e;
286                }
287             }
288          }
289 
290          if( operation != null )
291          {
292             WsdlMockOperation mockOperation = mockService.getMockOperation( operation );
293             if( mockOperation != null )
294             {
295                long startTime = System.nanoTime();
296                try
297                {
298                   result = mockOperation.dispatchRequest( mockRequest );
299                }
300                catch( DispatchException e )
301                {
302                   result = new WsdlMockResult( mockRequest );
303 
304                   String fault = SoapMessageBuilder.buildFault( "Server", e.getMessage(), mockRequest.getSoapVersion() );
305                   result.setResponseContent( fault );
306                   result.setMockOperation( mockOperation );
307 
308                   mockRequest.getHttpResponse().getWriter().write( fault );
309                }
310 
311                if( mockRequest.getHttpRequest() instanceof org.mortbay.jetty.Request )
312                   ( (org.mortbay.jetty.Request) mockRequest.getHttpRequest() ).setHandled( true );
313 
314                result.setTimeTaken( ( System.nanoTime() - startTime ) / 1000000 );
315                result.setTimestamp( timestamp );
316                addMockResult( result );
317                return result;
318             }
319             else
320             {
321                throw new DispatchException( "Failed to find matching operation for request" );
322             }
323          }
324 
325          throw new DispatchException( "Missing operation for soapAction [" + soapAction + "] and body element ["
326                  + XmlUtils.getQName( mockRequest.getContentElement().getDomNode() ) + "] with SOAP Version ["
327                  + mockRequest.getSoapVersion() + "]" );
328       }
329       catch( Exception e )
330       {
331          if( e instanceof DispatchException )
332             throw (DispatchException) e;
333 
334          throw new DispatchException( e );
335       }
336       finally
337       {
338          if( result != null )
339          {
340             for( MockRunListener listener : mockRunListeners )
341             {
342                listener.onMockResult( result );
343             }
344          }
345       }
346    }
347 
348    public MockResult getMockResultAt( int index )
349    {
350       return index <= removed ? null : mockResults.get( index - removed );
351    }
352 
353    public int getMockResultCount()
354    {
355       return mockResults.size() + removed;
356    }
357 
358    public synchronized void clearResults()
359    {
360       mockResults.clear();
361    }
362 
363    public void release()
364    {
365       clearResults();
366       mockService = null;
367       mockContext.clear();
368    }
369 
370    @Override
371    public MockResult dispatchRequest( HttpServletRequest request, HttpServletResponse response ) throws DispatchException
372    {
373       try
374       {
375          WsdlMockRequest mockRequest = new WsdlMockRequest( request, response, mockContext );
376          Object result = mockService.runOnRequestScript( mockContext, this, mockRequest );
377          if( !( result instanceof MockResult ) )
378          {
379             String method = request.getMethod();
380 
381             if( method.equals( "POST" ) )
382                result = dispatchPostRequest( mockRequest );
383             else
384                result = super.dispatchRequest( request, response );
385          }
386 
387          mockService.runAfterRequestScript( mockContext, this, (MockResult) result );
388          return (MockResult) result;
389       }
390       catch( Exception e )
391       {
392          throw new DispatchException( e );
393       }
394    }
395 
396    public MockResult dispatchGetRequest( HttpServletRequest request, HttpServletResponse response ) throws DispatchException
397    {
398       try
399       {
400          if( request.getQueryString() != null && request.getQueryString().startsWith( "WSDL" ) )
401          {
402             dispatchWsdlRequest( request, response );
403          }
404          else
405          {
406             String docroot = getMockService().getDocroot();
407             if( StringUtils.hasContent( docroot ) )
408             {
409                try
410                {
411                   File file = new File( docroot + request.getPathInfo().replace( '/', File.separatorChar ) );
412                   FileInputStream in = new FileInputStream( file );
413                   response.setStatus( HttpServletResponse.SC_OK );
414                   long length = file.length();
415                   response.setContentLength( (int) length );
416                   response.setContentType( ContentTypeHandler.getContentTypeFromFilename( file.getName() ) );
417                   Tools.readAndWrite( in, length, response.getOutputStream() );
418                }
419                catch( Exception e )
420                {
421                   throw new DispatchException( e );
422                }
423             }
424          }
425 
426          return null;
427       }
428       catch( Exception e )
429       {
430          throw new DispatchException( e );
431       }
432    }
433 
434    protected void dispatchWsdlRequest( HttpServletRequest request, HttpServletResponse response ) throws IOException
435    {
436       if( request.getQueryString().equalsIgnoreCase( "WSDL" ) )
437       {
438          printWsdl( response );
439          return;
440       }
441 
442       String ifaceName = request.getParameter( "interface" );
443       WsdlInterface iface = (WsdlInterface) mockService.getProject().getInterfaceByName( ifaceName );
444       if( iface == null )
445       {
446          printInterfaceList( response );
447          return;
448       }
449 
450       StringToStringMap parts = wsdlCache.get( iface.getName() );
451       String part = request.getParameter( "part" );
452       String content = StringUtils.isNullOrEmpty( part ) ? null : parts.get( part );
453 
454       if( content == null )
455       {
456          printPartList( iface, parts, response );
457          return;
458       }
459 
460       if( content != null )
461       {
462          printOkXmlResult( response, content );
463       }
464    }
465 
466    private void printOkXmlResult( HttpServletResponse response, String content ) throws IOException
467    {
468       response.setStatus( HttpServletResponse.SC_OK );
469       response.setContentType( "text/xml" );
470       response.setCharacterEncoding( "UTF-8" );
471       response.getWriter().print( content );
472    }
473 
474    private void printWsdl( HttpServletResponse response ) throws IOException
475    {
476       WsdlInterface[] mockedInterfaces = mockService.getMockedInterfaces();
477       if( mockedInterfaces.length == 1 )
478       {
479          StringToStringMap parts = wsdlCache.get( mockedInterfaces[0].getName() );
480          printOkXmlResult( response, parts.get( parts.get( "#root#" ) ) );
481       }
482       else
483       {
484          try
485          {
486             WSDLFactory wsdlFactory = WSDLFactory.newInstance();
487             Definition def = wsdlFactory.newDefinition();
488             for( WsdlInterface iface : mockedInterfaces )
489             {
490                StringToStringMap parts = wsdlCache.get( iface.getName() );
491                Import wsdlImport = def.createImport();
492                wsdlImport.setLocationURI( getInterfacePrefix( iface ) + "&part=" + parts.get( "#root#" ) );
493                wsdlImport.setNamespaceURI( iface.getWsdlContext().getDefinition().getTargetNamespace() );
494 
495                def.addImport( wsdlImport );
496             }
497 
498             response.setStatus( HttpServletResponse.SC_OK );
499             response.setContentType( "text/xml" );
500             response.setCharacterEncoding( "UTF-8" );
501 
502             WSDLWriter writer = wsdlFactory.newWSDLWriter();
503             writer.writeWSDL( def, response.getWriter() );
504          }
505          catch( Exception e )
506          {
507             SoapUI.logError( e );
508             throw new IOException( "Failed to create combined WSDL" );
509          }
510       }
511    }
512 
513    private void printPartList( WsdlInterface iface, StringToStringMap parts, HttpServletResponse response )
514            throws IOException
515    {
516       response.setStatus( HttpServletResponse.SC_OK );
517       response.setContentType( "text/html" );
518 
519       PrintWriter out = response.getWriter();
520       out.print( "<html><body><p>Parts in interface [" + iface.getName() + "]</p><ul>" );
521 
522       for( String key : parts.keySet() )
523       {
524          if( key.equals( "#root#" ) )
525             continue;
526 
527          out.print( "<li><a href=\"" );
528          out.print( getInterfacePrefix( iface ) + "&part=" + key );
529          out.print( "\">" + key + "</a></li>" );
530       }
531 
532       out.print( "</ul></p></body></html>" );
533    }
534 
535    private void printInterfaceList( HttpServletResponse response ) throws IOException
536    {
537       response.setStatus( HttpServletResponse.SC_OK );
538       response.setContentType( "text/html" );
539 
540       PrintWriter out = response.getWriter();
541       out.print( "<html><body><p>Mocked Interfaces in project [" + mockService.getProject().getName() + "]</p><ul>" );
542 
543       for( Interface iface : mockService.getProject().getInterfaceList() )
544       {
545          out.print( "<li><a href=\"" );
546          out.print( getInterfacePrefix( iface ) );
547          out.print( "\">" + iface.getName() + "</a></li>" );
548       }
549 
550       out.print( "</ul></p></body></html>" );
551    }
552 
553    public String getOverviewUrl()
554    {
555       return mockService.getPath() + "?WSDL";
556    }
557 
558    public void setLogEnabled( boolean logEnabled )
559    {
560       this.logEnabled = logEnabled;
561    }
562 }