View Javadoc

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