1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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
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 }