View Javadoc

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