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