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.monitor;
14  
15  import com.eviware.soapui.SoapUI;
16  import com.eviware.soapui.impl.wsdl.mock.DispatchException;
17  import com.eviware.soapui.impl.wsdl.mock.WsdlMockRunner;
18  import com.eviware.soapui.impl.wsdl.mock.WsdlMockService;
19  import com.eviware.soapui.impl.wsdl.support.soap.SoapMessageBuilder;
20  import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
21  import com.eviware.soapui.model.mock.MockResult;
22  import com.eviware.soapui.model.mock.MockRunner;
23  import com.eviware.soapui.model.mock.MockService;
24  import com.eviware.soapui.settings.HttpSettings;
25  import com.eviware.soapui.settings.SSLSettings;
26  import com.eviware.soapui.support.StringUtils;
27  import com.eviware.soapui.support.UISupport;
28  import com.eviware.soapui.support.log.JettyLogger;
29  import org.apache.log4j.Logger;
30  import org.mortbay.jetty.HttpConnection;
31  import org.mortbay.jetty.Server;
32  import org.mortbay.jetty.handler.AbstractHandler;
33  import org.mortbay.jetty.nio.SelectChannelConnector;
34  import org.mortbay.jetty.security.SslSocketConnector;
35  import org.mortbay.thread.BoundedThreadPool;
36  
37  import javax.servlet.ServletException;
38  import javax.servlet.http.HttpServletRequest;
39  import javax.servlet.http.HttpServletResponse;
40  import java.io.IOException;
41  import java.io.PrintWriter;
42  import java.util.*;
43  
44  /***
45   * Core Mock-Engine hosting a Jetty web server
46   * 
47   * @author ole.matzura
48   */
49  
50  public class MockEngine
51  {
52  	public final static Logger log = Logger.getLogger(MockEngine.class);
53  
54  	private Server server;
55  	private Map<Integer, Map<String, List<MockRunner>>> runners = new HashMap<Integer, Map<String, List<MockRunner>>>();
56  	private Map<Integer, SoapUIConnector> connectors = new HashMap<Integer, SoapUIConnector>();
57  	private List<MockRunner> mockRunners = new ArrayList<MockRunner>();
58  
59  	private SslSocketConnector sslConnector;
60  
61  	public MockEngine()
62  	{
63  		System.setProperty("org.mortbay.log.class", JettyLogger.class.getName());
64  	}
65  
66  	public boolean hasRunningMock(MockService mockService)
67  	{
68  		for (MockRunner runner : mockRunners)
69  			if (runner.getMockService() == mockService)
70  				return true;
71  
72  		return false;
73  	}
74  
75  	public synchronized void startMockService(MockRunner runner) throws Exception
76  	{
77  		if (server == null)
78  			initServer();
79  
80  		WsdlMockService mockService = (WsdlMockService) runner.getMockService();
81  		int port = mockService.getPort();
82  
83  		if (!runners.containsKey(port))
84  		{
85  			SoapUIConnector connector = new SoapUIConnector();
86  
87  			connector.setPort(port);
88  			if (sslConnector != null)
89  				connector.setConfidentialPort(sslConnector.getPort());
90  
91  			if (mockService.getBindToHostOnly())
92  			{
93  				String host = mockService.getHost();
94  				if (StringUtils.hasContent(host))
95  				{
96  					connector.setHost(host);
97  				}
98  			}
99  
100 			boolean wasRunning = server.isRunning();
101 
102 			if (wasRunning)
103 			{
104 				server.stop();
105 			}
106 
107 			server.addConnector(connector);
108 			try
109 			{
110 				server.start();
111 			}
112 			catch (RuntimeException e)
113 			{
114 				UISupport.showErrorMessage(e);
115 
116 				server.removeConnector(connector);
117 				if (wasRunning)
118 				{
119 					server.start();
120 					return;
121 				}
122 			}
123 
124 			connectors.put(new Integer(port), connector);
125 			runners.put(new Integer(port), new HashMap<String, List<MockRunner>>());
126 		}
127 
128 		Map<String, List<MockRunner>> map = runners.get(port);
129 		String path = mockService.getPath();
130 		if (!map.containsKey(path))
131 		{
132 			map.put(path, new ArrayList<MockRunner>());
133 		}
134 		map.get(path).add(runner);
135 		mockRunners.add(runner);
136 
137 		log.info("Started mockService [" + mockService.getName() + "] on port [" + port + "] at path [" + path + "]");
138 	}
139 
140 	private void initServer() throws Exception
141 	{
142 		server = new Server();
143 		BoundedThreadPool threadPool = new BoundedThreadPool();
144 		threadPool.setMaxThreads(100);
145 		server.setThreadPool(threadPool);
146 		server.setHandler(new ServerHandler());
147 
148 		if (SoapUI.getSettings().getBoolean(SSLSettings.ENABLE_MOCK_SSL))
149 		{
150 			sslConnector = new SslSocketConnector();
151 			sslConnector.setKeystore(SoapUI.getSettings().getString(SSLSettings.MOCK_KEYSTORE, null));
152 			sslConnector.setPassword(SoapUI.getSettings().getString(SSLSettings.MOCK_PASSWORD, null));
153 			sslConnector.setKeyPassword(SoapUI.getSettings().getString(SSLSettings.MOCK_KEYSTORE_PASSWORD, null));
154 			sslConnector.setTruststore(SoapUI.getSettings().getString(SSLSettings.MOCK_TRUSTSTORE, null));
155 			sslConnector.setTrustPassword(SoapUI.getSettings().getString(SSLSettings.MOCK_TRUSTSTORE_PASSWORD, null));
156 			sslConnector.setMaxIdleTime(30000);
157 			sslConnector.setPort((int) SoapUI.getSettings().getLong(SSLSettings.MOCK_PORT, 443));
158 			sslConnector.setNeedClientAuth(SoapUI.getSettings().getBoolean(SSLSettings.CLIENT_AUTHENTICATION));
159 
160 			server.addConnector(sslConnector);
161 		}
162 	}
163 
164 	public synchronized void stopMockService(WsdlMockRunner runner)
165 	{
166 		MockService mockService = runner.getMockService();
167 		final Integer port = new Integer(mockService.getPort());
168 		Map<String, List<MockRunner>> map = runners.get(port);
169 
170 		map.get(mockService.getPath()).remove(runner);
171 		mockRunners.remove(runner);
172 
173 		log.info("Stopped MockService [" + mockService.getName() + "] on port [" + port + "]");
174 
175 		if (map.isEmpty() && !SoapUI.getSettings().getBoolean(HttpSettings.LEAVE_MOCKENGINE))
176 		{
177 			SoapUIConnector connector = (SoapUIConnector) connectors.get(port);
178 			if (connector == null)
179 			{
180 				log.warn("Missing connectors on port [" + port + "]");
181 				return;
182 			}
183 
184 			try
185 			{
186 				log.info("Stopping connector on port " + port);
187 				if (!connector.waitUntilIdle(5000))
188 				{
189 					log.warn("Failed to wait for idle.. stopping connector anyway..");
190 				}
191 				connector.stop();
192 			}
193 			catch (Exception e)
194 			{
195 				SoapUI.logError(e);
196 			}
197 			server.removeConnector(connector);
198 			runners.remove(port);
199 			if (runners.isEmpty())
200 			{
201 				try
202 				{
203 					log.info("No more connectors.. stopping server");
204 					server.stop();
205 					if (sslConnector != null)
206 					{
207 						server.removeConnector(sslConnector);
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, HttpServletResponse response, int dispatch)
265 				throws IOException, ServletException
266 		{
267 			// find mockService
268 			Map<String, List<MockRunner>> map = runners.get(request.getLocalPort());
269 
270 			// ssl?
271 			if (map == null && sslConnector != null && request.getLocalPort() == sslConnector.getPort())
272 			{
273 				for (Map<String, List<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 				List<MockRunner> wsdlMockRunners = map.get(request.getPathInfo());
286 				if (wsdlMockRunners == null && request.getMethod().equals("GET"))
287 				{
288 					for (String root : map.keySet())
289 					{
290 						if (request.getPathInfo().startsWith(root))
291 						{
292 							wsdlMockRunners = map.get(root);
293 						}
294 					}
295 				}
296 
297 				if (wsdlMockRunners != null)
298 				{
299 					synchronized (wsdlMockRunners)
300 					{
301 						try
302 						{
303                      DispatchException ex = null;
304 
305 							for (MockRunner wsdlMockRunner : wsdlMockRunners)
306 							{
307 								try
308 								{
309 									MockResult result = wsdlMockRunner.dispatchRequest(request, response);
310 									if( result != null ) 
311 										result.finish();
312 
313 									// if we get here, we got dispatched..
314 									break;
315 								}
316 								catch (DispatchException e)
317 								{
318 									log.debug(wsdlMockRunner.getMockService().getName()
319 											+ " was unable to dispatch mock request ", e);
320 
321                            ex = e;
322 								}
323 							}
324 
325                      if( ex != null )
326                         throw ex;
327 						}
328 						catch (Exception e)
329 						{
330 							SoapUI.logError(e);
331 
332 							response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
333 							response.setContentType("text/html");
334 							response.getWriter().print(
335 									SoapMessageBuilder.buildFault("Server", e.getMessage(), SoapVersion.Utils
336 											.getSoapVersionForContentType(request.getContentType(), SoapVersion.Soap11)));
337 							response.flushBuffer();
338 
339 							// throw new ServletException( e );
340 						}
341 					}
342 				}
343 				else
344 				{
345 					printMockServiceList(response);
346 				}
347 			}
348 			else
349 			{
350 				printMockServiceList(response);
351 			}
352 		}
353 
354 		private void printMockServiceList(HttpServletResponse response) throws IOException
355 		{
356 			response.setStatus(HttpServletResponse.SC_OK);
357 			response.setContentType("text/html");
358 
359 			MockRunner[] mockRunners = getMockRunners();
360 			PrintWriter out = response.getWriter();
361 			out.print("<html><body><p>There are currently " + mockRunners.length + " running soapUI MockServices</p><ul>");
362 
363 			for (MockRunner mockRunner : mockRunners)
364 			{
365 				out.print("<li><a href=\"");
366 				out.print(((WsdlMockService) mockRunner.getMockService()).getPath() + "?WSDL");
367 				out.print("\">" + mockRunner.getMockService().getName() + "</a></li>");
368 			}
369 
370 			out.print("</ul></p></body></html>");
371 			response.flushBuffer();
372 		}
373 	}
374 
375 	public MockRunner[] getMockRunners()
376 	{
377 		return mockRunners.toArray(new MockRunner[mockRunners.size()]);
378 	}
379 }