1
2
3
4
5
6
7
8
9
10
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
268 Map<String, MockRunner> map = runners.get( request.getLocalPort() );
269
270
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
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 }