View Javadoc

1   package org.eclipse.jetty.http.spi;
2   
3   //========================================================================
4   //Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
5   //------------------------------------------------------------------------
6   //All rights reserved. This program and the accompanying materials
7   //are made available under the terms of the Eclipse Public License v1.0
8   //and Apache License v2.0 which accompanies this distribution.
9   //The Eclipse Public License is available at 
10  //http://www.eclipse.org/legal/epl-v10.html
11  //The Apache License v2.0 is available at
12  //http://www.opensource.org/licenses/apache2.0.php
13  //You may elect to redistribute this code under either of these licenses. 
14  //========================================================================
15  
16  import com.sun.net.httpserver.HttpContext;
17  import com.sun.net.httpserver.HttpHandler;
18  import org.eclipse.jetty.server.Connector;
19  import org.eclipse.jetty.server.Handler;
20  import org.eclipse.jetty.server.Server;
21  import org.eclipse.jetty.server.handler.ContextHandler;
22  import org.eclipse.jetty.server.handler.HandlerCollection;
23  import org.eclipse.jetty.server.handler.ContextHandlerCollection;
24  import org.eclipse.jetty.server.nio.SelectChannelConnector;
25  import org.eclipse.jetty.util.log.Log;
26  import org.eclipse.jetty.util.log.Logger;
27  
28  
29  import java.io.IOException;
30  import java.net.InetSocketAddress;
31  import java.util.HashMap;
32  import java.util.Map;
33  import java.util.concurrent.Executor;
34  import java.util.concurrent.ThreadPoolExecutor;
35  
36  /**
37   * Jetty implementation of {@link com.sun.net.httpserver.HttpServer}.
38   */
39  public class JettyHttpServer extends com.sun.net.httpserver.HttpServer
40  {
41      private static final Logger LOG = Log.getLogger(JettyHttpServer.class);
42  
43  
44      private Server _server;
45      
46      private boolean _serverShared;
47  
48      private InetSocketAddress _addr;
49  
50      private ThreadPoolExecutor _executor;
51  
52      private Map<String, JettyHttpContext> _contexts = new HashMap<String, JettyHttpContext>();
53      
54      private Map<String, Connector> _connectors = new HashMap<String, Connector>();
55  
56      
57      public JettyHttpServer(Server server, boolean shared)
58      {
59          this._server = server;
60          this._serverShared = shared;
61      }
62  
63      @Override
64      public void bind(InetSocketAddress addr, int backlog) throws IOException
65      {
66      	// check if there is already a connector listening
67          Connector[] connectors = _server.getConnectors();
68          if (connectors != null)
69          {
70              for (Connector connector : connectors)
71              {
72                  if (connector.getPort() == addr.getPort()) {
73                      if (LOG.isDebugEnabled()) LOG.debug("server already bound to port " + addr.getPort() + ", no need to rebind");
74                      return;
75                  }
76              }
77          }
78          
79          if (_serverShared)
80          	throw new IOException("jetty server is not bound to port " + addr.getPort());
81          
82          this._addr = addr;
83  
84          if (LOG.isDebugEnabled()) LOG.debug("binding server to port " + addr.getPort());
85          SelectChannelConnector connector = new SelectChannelConnector();
86          connector.setAcceptors(1);
87          connector.setPort(addr.getPort());
88          connector.setHost(addr.getHostName());
89          _server.addConnector(connector);
90          
91          _connectors.put(addr.getHostName() + addr.getPort(), connector);
92      }
93  
94      @Override
95      public InetSocketAddress getAddress()
96      {
97          return _addr;
98      }
99  
100     @Override
101     public void start()
102     {
103     	if (_serverShared) return;
104     	
105         try
106         {
107             _server.start();
108         }
109         catch (Exception ex)
110         {
111             throw new RuntimeException(ex);
112         }
113     }
114 
115     @Override
116     public void setExecutor(Executor executor)
117     {
118         if (executor == null)
119             throw new IllegalArgumentException("missing required 'executor' argument");
120 
121     	if (!(executor instanceof ThreadPoolExecutor))
122     		throw new IllegalArgumentException("only java.util.concurrent.ThreadPoolExecutor instances are allowed, got: " + executor.getClass().getName());
123     	
124     	if (LOG.isDebugEnabled()) LOG.debug("using ThreadPoolExecutor for server thread pool");
125     	this._executor = (ThreadPoolExecutor) executor;
126     	_server.setThreadPool(new ThreadPoolExecutorAdapter(_executor));
127     }
128 
129     @Override
130     public Executor getExecutor()
131     {
132         return _executor;
133     }
134 
135     @Override
136     public void stop(int delay)
137     {
138     	cleanUpContexts();
139     	cleanUpConnectors();
140     	
141     	if (_serverShared) return;
142 
143     	try
144         {
145             _server.stop();
146         }
147         catch (Exception ex)
148         {
149             throw new RuntimeException(ex);
150         }
151     }
152 
153 	private void cleanUpContexts()
154 	{
155         for (Map.Entry<String, JettyHttpContext> stringJettyHttpContextEntry : _contexts.entrySet())
156         {
157             JettyHttpContext context = stringJettyHttpContextEntry.getValue();
158             _server.removeBean(context.getJettyContextHandler());
159         }
160 		_contexts.clear();
161 	}
162 
163     private void cleanUpConnectors()
164     {
165         for (Map.Entry<String, Connector> stringConnectorEntry : _connectors.entrySet())
166         {
167             Connector connector = stringConnectorEntry.getValue();
168             try
169             {
170                 connector.stop();
171             } catch (Exception ex) {
172                 LOG.warn(ex);
173             }
174             _server.removeConnector(connector);
175         }
176 		_connectors.clear();
177 	}
178 
179 	@Override
180     public HttpContext createContext(String path, HttpHandler httpHandler)
181     {
182     	checkIfContextIsFree(path);
183 
184         JettyHttpContext context = new JettyHttpContext(this, path, httpHandler);
185         HttpSpiContextHandler jettyContextHandler = context.getJettyContextHandler();
186 
187         ContextHandlerCollection chc = findContextHandlerCollection(_server.getHandlers());
188         if (chc == null)
189         	throw new RuntimeException("could not find ContextHandlerCollection, you must configure one");
190 
191         chc.addHandler(jettyContextHandler);
192         _contexts.put(path, context);
193 
194         return context;
195     }
196 
197     private ContextHandlerCollection findContextHandlerCollection(Handler[] handlers)
198     {
199         if (handlers == null)
200             return null;
201 
202         for (Handler handler : handlers)
203         {
204             if (handler instanceof ContextHandlerCollection)
205             {
206                 return (ContextHandlerCollection) handler;
207             }
208 
209             if (handler instanceof HandlerCollection)
210             {
211                 HandlerCollection hc = (HandlerCollection) handler;
212                 ContextHandlerCollection chc = findContextHandlerCollection(hc.getHandlers());
213                 if (chc != null)
214                     return chc;
215             }
216         }
217         return null;
218     }
219 
220     private void checkIfContextIsFree(String path)
221     {
222     	Handler serverHandler = _server.getHandler();
223 		if (serverHandler instanceof ContextHandler)
224 		{
225 			ContextHandler ctx = (ContextHandler) serverHandler;
226 			if (ctx.getContextPath().equals(path))
227 	        	throw new RuntimeException("another context already bound to path " + path);
228 		}
229     	
230     	Handler[] handlers = _server.getHandlers();
231     	if (handlers == null) return;
232 
233         for (Handler handler : handlers)
234         {
235             if (handler instanceof ContextHandler) {
236                 ContextHandler ctx = (ContextHandler) handler;
237                 if (ctx.getContextPath().equals(path))
238                     throw new RuntimeException("another context already bound to path " + path);
239             }
240         }
241 	}
242 
243 	@Override
244     public HttpContext createContext(String path)
245     {
246         return createContext(path, null);
247     }
248 
249     @Override
250     public void removeContext(String path) throws IllegalArgumentException
251     {
252         JettyHttpContext context = _contexts.remove(path);
253         if (context == null) return;
254         _server.removeBean(context.getJettyContextHandler());
255     }
256 
257     @Override
258     public void removeContext(HttpContext context)
259     {
260         removeContext(context.getPath());
261     }
262 
263 }