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