View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2007 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.monitor;
14  
15  import java.io.IOException;
16  import java.io.PrintWriter;
17  import java.util.ArrayList;
18  import java.util.HashMap;
19  import java.util.HashSet;
20  import java.util.List;
21  import java.util.Map;
22  import java.util.Set;
23  
24  import javax.servlet.ServletException;
25  import javax.servlet.http.HttpServletRequest;
26  import javax.servlet.http.HttpServletResponse;
27  
28  import org.apache.log4j.Logger;
29  import org.mortbay.jetty.HttpConnection;
30  import org.mortbay.jetty.Server;
31  import org.mortbay.jetty.handler.AbstractHandler;
32  import org.mortbay.jetty.nio.SelectChannelConnector;
33  import org.mortbay.jetty.security.SslSocketConnector;
34  import org.mortbay.thread.BoundedThreadPool;
35  
36  import com.eviware.soapui.SoapUI;
37  import com.eviware.soapui.impl.wsdl.mock.WsdlMockResult;
38  import com.eviware.soapui.impl.wsdl.mock.WsdlMockRunner;
39  import com.eviware.soapui.impl.wsdl.mock.WsdlMockService;
40  import com.eviware.soapui.impl.wsdl.support.soap.SoapMessageBuilder;
41  import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
42  import com.eviware.soapui.model.mock.MockRunner;
43  import com.eviware.soapui.model.mock.MockService;
44  import com.eviware.soapui.settings.HttpSettings;
45  import com.eviware.soapui.settings.SSLSettings;
46  import com.eviware.soapui.support.StringUtils;
47  import com.eviware.soapui.support.UISupport;
48  import com.eviware.soapui.support.log.JettyLogger;
49  
50  /***
51   * Core Mock-Engine hosting a Jetty web server
52   * 
53   * @author ole.matzura
54   */
55  
56  public class MockEngine
57  {
58  	public final static Logger log = Logger.getLogger( MockEngine.class );
59  	
60  	private Server server;
61  	private Map<Integer, Map<String,MockRunner> > runners = new HashMap<Integer, Map<String,MockRunner> >();
62  	private Map<Integer, SoapUIConnector> connectors = new HashMap<Integer,SoapUIConnector>();
63  	private List<MockRunner> mockRunners = new ArrayList<MockRunner>();
64  
65  	private SslSocketConnector sslConnector;
66  	
67  	public MockEngine()
68  	{
69  		System.setProperty( "org.mortbay.log.class", JettyLogger.class.getName() );
70  	}
71  	
72  	public boolean hasRunningMock( MockService mockService )
73  	{
74  		for( MockRunner runner : mockRunners )
75  			if( runner.getMockService() == mockService )
76  				return true;
77  		
78  		return false;
79  	}
80  
81  	public void startMockService( MockRunner runner ) throws Exception
82  	{
83  		if( server == null )
84  			initServer();
85  		
86  		WsdlMockService mockService = ( WsdlMockService ) runner.getMockService();
87  		int port = mockService.getPort();
88  		
89  		if( !runners.containsKey( port ))
90  		{
91  			SoapUIConnector connector = new SoapUIConnector();
92  			
93  			connector.setPort( port );
94  			if( sslConnector != null )
95  				connector.setConfidentialPort( sslConnector.getPort() );
96  			
97  			if( mockService.getBindToHostOnly() )
98  			{
99  				String host = mockService.getHost();
100 				if( StringUtils.hasContent( host ))
101 				{
102 					connector.setHost( host );
103 				}
104 			}
105 			
106 			boolean wasRunning = server.isRunning();
107 			
108 			if( wasRunning )
109 			{
110 				server.stop();
111 			}
112 			
113 			server.addConnector( connector );
114 			try
115 			{
116 				server.start();
117 			}
118 			catch( RuntimeException e )
119 			{
120 				UISupport.showErrorMessage( e );
121 				
122 				server.removeConnector( connector );
123 				if( wasRunning ) 
124 				{
125 					server.start();
126 					return;
127 				}
128 			}
129 			
130 			connectors.put( new Integer( port), connector );
131 			runners.put( new Integer( port), new HashMap<String,MockRunner>() );
132 		}
133 		
134 		Map<String, MockRunner> map = runners.get( port );
135 		String path = mockService.getPath();
136 		map.put( path, runner );
137 		mockRunners.add( runner );
138 		
139 		log.info(  "Started mockService [" + mockService.getName() + "] on port [" + port + "] at path [" + path + "]" );
140 	}
141 
142 	private void initServer() throws Exception
143 	{
144 		server = new Server();
145 		BoundedThreadPool threadPool = new BoundedThreadPool();
146 		threadPool.setMaxThreads( 100 );
147 		server.setThreadPool( threadPool );
148 		server.setHandler( new ServerHandler() );
149 		
150 		if( SoapUI.getSettings().getBoolean( SSLSettings.ENABLE_MOCK_SSL ))
151 		{
152 			sslConnector = new SslSocketConnector();
153 			sslConnector.setKeystore( SoapUI.getSettings().getString( SSLSettings.MOCK_KEYSTORE, null ));
154 			sslConnector.setPassword( SoapUI.getSettings().getString( SSLSettings.MOCK_PASSWORD, null ));
155 			sslConnector.setKeyPassword( SoapUI.getSettings().getString( SSLSettings.MOCK_KEYSTORE_PASSWORD, null ));
156 			sslConnector.setTruststore( SoapUI.getSettings().getString( SSLSettings.MOCK_TRUSTSTORE, null ));
157 			sslConnector.setTrustPassword( SoapUI.getSettings().getString( SSLSettings.MOCK_TRUSTSTORE_PASSWORD, null ));
158 			sslConnector.setMaxIdleTime( 30000 );
159 			sslConnector.setPort( ( int ) SoapUI.getSettings().getLong( SSLSettings.MOCK_PORT, 443 ) );
160 			
161 			server.addConnector( sslConnector );
162 		}
163 	}
164 
165 	public void stopMockService( WsdlMockRunner runner )
166 	{
167 		MockService mockService = runner.getMockService();
168 		final Integer port = new Integer( mockService.getPort());
169 		Map<String, MockRunner> map = runners.get( port );
170 		
171 		map.remove( mockService.getPath() );
172 		mockRunners.remove( runner );
173 		
174 		log.info(  "Stopped MockService [" + mockService.getName() + "] on port [" + port + "]" );
175 		
176 		if( map.isEmpty() && !SoapUI.getSettings().getBoolean( HttpSettings.LEAVE_MOCKENGINE ) )
177 		{
178 			SoapUIConnector connector = ( SoapUIConnector ) connectors.get( port );
179 			if( connector == null )
180 			{
181 				log.warn( "Missing connectors on port [" + port + "]" );
182 				return;
183 			}
184 			
185 			try
186 			{
187 				log.info( "Stopping connector on port " + port );
188 				if( !connector.waitUntilIdle( 5000 ))
189 				{
190 					log.warn(  "Failed to wait for idle.. stopping connector anyway.." );
191 				}
192 				connector.stop();
193 			}
194 			catch( Exception e )
195 			{
196 				SoapUI.logError( e );
197 			}
198 			server.removeConnector( connector );
199 			runners.remove( port );
200 			if( runners.isEmpty() )
201 			{
202 				try
203 				{
204 					log.info( "No more connectors.. stopping server" );
205 					server.stop();
206 					if( sslConnector != null )
207 					{
208 						sslConnector.stop();
209 						sslConnector = null;
210 					}
211 				}
212 				catch( Exception e )
213 				{
214 					SoapUI.logError( e );
215 				}
216 			}
217 		}
218 	}
219 	
220 	private class SoapUIConnector extends SelectChannelConnector
221 	{
222 		private Set<HttpConnection> connections = new HashSet<HttpConnection>();
223 		
224 		@Override
225 		protected void connectionClosed( HttpConnection arg0 )
226 		{
227 			super.connectionClosed( arg0 );
228 			connections.remove( arg0 );
229 		}
230 
231 		@Override
232 		protected void connectionOpened( HttpConnection arg0 )
233 		{
234 			super.connectionOpened( arg0 );
235 			connections.add( arg0 );
236 		}
237 		
238 		public boolean waitUntilIdle( long maxwait ) throws Exception
239 		{
240 			while( maxwait > 0 && hasActiveConnections() )
241 			{
242 				System.out.println( "Waiting for active connections to finish.." );
243 				Thread.sleep( 500 );
244 				maxwait -= 500;
245 			}
246 			
247 			return !hasActiveConnections();
248 		}
249 
250 		private boolean hasActiveConnections()
251 		{
252 			for( HttpConnection connection : connections )
253 			{
254 				if( !connection.isIdle() )
255 					return true;
256 			}
257 			
258 			return false;
259 		}
260 	}
261 	
262 	private class ServerHandler extends AbstractHandler
263 	{
264 		public void handle( String target, HttpServletRequest request,
265 					HttpServletResponse response, int dispatch ) throws IOException, ServletException
266 		{
267 			// find mockService
268 			Map<String, MockRunner> map = runners.get( request.getLocalPort() );
269 			
270 			// ssl?
271 			if( map == null && sslConnector != null && request.getLocalPort() == sslConnector.getPort())
272 			{
273 				for( Map<String, MockRunner> runnerMap : runners.values() )
274 				{
275 					if( runnerMap.containsKey( request.getPathInfo() ))
276 					{
277 						map = runnerMap;
278 						break;
279 					}
280 				}
281 			}
282 			
283 			if( map != null )
284 			{
285 				MockRunner wsdlMockRunner = map.get( request.getPathInfo() );
286 				if( wsdlMockRunner != null )
287 				{
288 					try
289 					{
290 						if( request.getMethod().equals( "GET" ) && request.getQueryString() != null && request.getQueryString().startsWith( "WSDL" ))
291 						{
292 							wsdlMockRunner.dispatchWsdlRequest( request, response );
293 						}
294 						else if( request.getMethod().equals( "POST" ))
295 						{
296 							WsdlMockResult result = ( WsdlMockResult ) wsdlMockRunner.dispatchMockRequest( request, response );
297 							result.finish();
298 						}
299 						else if( request.getMethod().equals( "GET" ) )
300 						{
301 							printMockServiceList( response );
302 						}
303 						else
304 						{
305 							throw new Exception( "Failed to dispatch request; " + request.toString() );
306 						}
307 					}
308 					catch( Exception e )
309 					{
310 						SoapUI.logError( e );
311 						
312 						response.setStatus( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
313 						response.setContentType( "text/html" );
314 						response.getWriter().print( SoapMessageBuilder.buildFault( "Server", e.getMessage(), 
315 									SoapVersion.Utils.getSoapVersionForContentType( request.getContentType(), SoapVersion.Soap11 )));
316 						response.flushBuffer();
317 						
318 						//throw new ServletException( e );
319 					}
320 				}
321 				else
322 				{
323 					printMockServiceList( response );
324 				}
325 			}
326 			else
327 			{
328 				printMockServiceList( response );
329 			}
330 		}
331 
332 		private void printMockServiceList( HttpServletResponse response ) throws IOException
333 		{
334 			response.setStatus( HttpServletResponse.SC_OK );
335 			response.setContentType( "text/html" );
336 
337 			MockRunner[] mockRunners = getMockRunners();
338 			PrintWriter out = response.getWriter();
339 			out.print( "<html><body><p>There are currently " + mockRunners.length + " running soapUI MockServices</p><ul>" );
340 			
341 			for( MockRunner mockRunner : mockRunners )
342 			{
343 				out.print( "<li><a href=\"" );
344 				out.print( ((WsdlMockService)mockRunner.getMockService()).getPath() + "?WSDL" );
345 				out.print( "\">" + mockRunner.getMockService().getName() + "</a></li>" );
346 			}
347 
348 			out.print( "</ul></p></body></html>" );
349 			response.flushBuffer();
350 		}
351 	}
352 
353 	public MockRunner [] getMockRunners()
354 	{
355 		return mockRunners.toArray( new MockRunner[mockRunners.size()] );
356 	}
357 }