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