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.util.ArrayList;
17  import java.util.HashMap;
18  import java.util.HashSet;
19  import java.util.List;
20  import java.util.Map;
21  import java.util.Set;
22  
23  import javax.servlet.ServletException;
24  import javax.servlet.http.HttpServletRequest;
25  import javax.servlet.http.HttpServletResponse;
26  
27  import org.apache.log4j.Logger;
28  import org.mortbay.jetty.HttpConnection;
29  import org.mortbay.jetty.Server;
30  import org.mortbay.jetty.handler.AbstractHandler;
31  import org.mortbay.jetty.nio.SelectChannelConnector;
32  import org.mortbay.thread.BoundedThreadPool;
33  
34  import com.eviware.soapui.SoapUI;
35  import com.eviware.soapui.impl.wsdl.mock.WsdlMockResult;
36  import com.eviware.soapui.impl.wsdl.mock.WsdlMockRunner;
37  import com.eviware.soapui.impl.wsdl.support.soap.SoapMessageBuilder;
38  import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
39  import com.eviware.soapui.model.mock.MockRunner;
40  import com.eviware.soapui.model.mock.MockService;
41  import com.eviware.soapui.support.UISupport;
42  import com.eviware.soapui.support.log.JettyLogger;
43  
44  /***
45   * Core Mock-Engine hosting a Jetty web server
46   * 
47   * @author ole.matzura
48   */
49  
50  public class MockEngine
51  {
52  	private final static Logger log = Logger.getLogger( MockEngine.class );
53  	
54  	private Server server;
55  	private Map<Integer, Map<String,MockRunner> > runners = new HashMap<Integer, Map<String,MockRunner> >();
56  	private Map<Integer, SoapUIConnector> connectors = new HashMap<Integer,SoapUIConnector>();
57  	private List<MockRunner> mockRunners = new ArrayList<MockRunner>();
58  	
59  	public MockEngine()
60  	{
61  		System.setProperty( "org.mortbay.log.class", JettyLogger.class.getName() );
62  	}
63  	
64  	public boolean hasRunningMock( MockService mockService )
65  	{
66  		for( MockRunner runner : mockRunners )
67  			if( runner.getMockService() == mockService )
68  				return true;
69  		
70  		return false;
71  	}
72  
73  	public void startMockService( MockRunner runner ) throws Exception
74  	{
75  		if( server == null )
76  			initServer();
77  		
78  		MockService mockService = runner.getMockService();
79  		int port = mockService.getPort();
80  		
81  		if( !runners.containsKey( port ))
82  		{
83  			SoapUIConnector connector = new SoapUIConnector();
84  			
85  			connector.setPort( port );
86  			boolean wasRunning = server.isRunning();
87  			
88  			if( wasRunning )
89  			{
90  				server.stop();
91  			}
92  			
93  			server.addConnector( connector );
94  			try
95  			{
96  				server.start();
97  			}
98  			catch( RuntimeException e )
99  			{
100 				UISupport.showErrorMessage( e );
101 				
102 				server.removeConnector( connector );
103 				if( wasRunning ) 
104 				{
105 					server.start();
106 					return;
107 				}
108 			}
109 			
110 			connectors.put( new Integer( port), connector );
111 			runners.put( new Integer( port), new HashMap<String,MockRunner>() );
112 		}
113 		
114 		Map<String, MockRunner> map = runners.get( port );
115 		String path = mockService.getPath();
116 		map.put( path, runner );
117 		mockRunners.add( runner );
118 		
119 		log.info(  "Started mockService [" + mockService.getName() + "] on port [" + port + "] at path [" + path + "]" );
120 	}
121 
122 	private void initServer() throws Exception
123 	{
124 		server = new Server();
125 		BoundedThreadPool threadPool = new BoundedThreadPool();
126 		threadPool.setMaxThreads( 100 );
127 		server.setThreadPool( threadPool );
128 
129 		server.setHandler( new ServerHandler() );
130 	}
131 
132 	public void stopMockService( WsdlMockRunner runner )
133 	{
134 		MockService mockService = runner.getMockService();
135 		final Integer port = new Integer( mockService.getPort());
136 		Map<String, MockRunner> map = runners.get( port );
137 		
138 		map.remove( mockService.getPath() );
139 		mockRunners.remove( runner );
140 		
141 		if( map.isEmpty() )
142 		{
143 			SoapUIConnector connector = ( SoapUIConnector ) connectors.get( port );
144 			if( connector == null )
145 			{
146 				log.warn( "Missing connectors on port [" + port + "]" );
147 				return;
148 			}
149 			
150 			try
151 			{
152 				log.info( "Stopping connector on port " + port );
153 				if( !connector.waitUntilIdle( 5000 ))
154 				{
155 					log.warn(  "Failed to wait for idle.. stopping connector anyway.." );
156 				}
157 				connector.stop();
158 			}
159 			catch( Exception e )
160 			{
161 				SoapUI.logError( e );
162 			}
163 			server.removeConnector( connector );
164 			runners.remove( port );
165 			if( runners.isEmpty() )
166 			{
167 				try
168 				{
169 					log.info( "No more connectors.. stopping server" );
170 					server.stop();
171 				}
172 				catch( Exception e )
173 				{
174 					SoapUI.logError( e );
175 				}
176 			}
177 		}
178 	}
179 	
180 	private class SoapUIConnector extends SelectChannelConnector
181 	{
182 		private Set<HttpConnection> connections = new HashSet<HttpConnection>();
183 		
184 		@Override
185 		protected void connectionClosed( HttpConnection arg0 )
186 		{
187 			super.connectionClosed( arg0 );
188 			connections.remove( arg0 );
189 		}
190 
191 		@Override
192 		protected void connectionOpened( HttpConnection arg0 )
193 		{
194 			super.connectionOpened( arg0 );
195 			connections.add( arg0 );
196 		}
197 		
198 		public boolean waitUntilIdle( long maxwait ) throws Exception
199 		{
200 			while( maxwait > 0 && hasActiveConnections() )
201 			{
202 				System.out.println( "Waiting for active connections to finish.." );
203 				Thread.sleep( 500 );
204 				maxwait -= 500;
205 			}
206 			
207 			return !hasActiveConnections();
208 		}
209 
210 		private boolean hasActiveConnections()
211 		{
212 			for( HttpConnection connection : connections )
213 			{
214 				if( !connection.isIdle() )
215 					return true;
216 			}
217 			
218 			return false;
219 		}
220 	}
221 	
222 	private class ServerHandler extends AbstractHandler
223 	{
224 		public void handle( String target, HttpServletRequest request,
225 					HttpServletResponse response, int dispatch ) throws IOException, ServletException
226 		{
227 			// find mockService
228 			Map<String, MockRunner> map = runners.get( request.getLocalPort() );
229 			if( map != null )
230 			{
231 				MockRunner wsdlMockRunner = map.get( request.getPathInfo() );
232 				if( wsdlMockRunner != null )
233 				{
234 					try
235 					{
236 						WsdlMockResult result = ( WsdlMockResult ) wsdlMockRunner.dispatchRequest( request, response );
237 						result.finish();
238 					}
239 					catch( Exception e )
240 					{
241 						SoapUI.logError( e );
242 						
243 						response.getWriter().print( SoapMessageBuilder.buildFault( "Server", e.getMessage(), 
244 									SoapVersion.Utils.getSoapVersionForContentType( request.getContentType() )));
245 						
246 						throw new ServletException( e );
247 					}
248 				}
249 			}
250 		}
251 	}
252 
253 	public MockRunner [] getMockRunners()
254 	{
255 		return mockRunners.toArray( new MockRunner[mockRunners.size()] );
256 	}
257 }