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.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
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
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 }