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