1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.server;
20
21 import java.io.IOException;
22 import java.net.InetSocketAddress;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.Date;
27 import java.util.Enumeration;
28 import java.util.List;
29 import java.util.concurrent.CopyOnWriteArrayList;
30 import java.util.concurrent.Future;
31 import java.util.concurrent.TimeUnit;
32
33 import javax.servlet.ServletContext;
34 import javax.servlet.ServletException;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37
38 import org.eclipse.jetty.http.HttpGenerator;
39 import org.eclipse.jetty.http.HttpHeader;
40 import org.eclipse.jetty.http.HttpMethod;
41 import org.eclipse.jetty.http.HttpStatus;
42 import org.eclipse.jetty.http.HttpURI;
43 import org.eclipse.jetty.server.handler.HandlerWrapper;
44 import org.eclipse.jetty.util.Attributes;
45 import org.eclipse.jetty.util.AttributesMap;
46 import org.eclipse.jetty.util.Jetty;
47 import org.eclipse.jetty.util.MultiException;
48 import org.eclipse.jetty.util.URIUtil;
49 import org.eclipse.jetty.util.annotation.ManagedAttribute;
50 import org.eclipse.jetty.util.annotation.ManagedObject;
51 import org.eclipse.jetty.util.annotation.Name;
52 import org.eclipse.jetty.util.component.Graceful;
53 import org.eclipse.jetty.util.component.LifeCycle;
54 import org.eclipse.jetty.util.log.Log;
55 import org.eclipse.jetty.util.log.Logger;
56 import org.eclipse.jetty.util.thread.QueuedThreadPool;
57 import org.eclipse.jetty.util.thread.ShutdownThread;
58 import org.eclipse.jetty.util.thread.ThreadPool;
59
60
61
62
63
64
65
66
67 @ManagedObject(value="Jetty HTTP Servlet server")
68 public class Server extends HandlerWrapper implements Attributes
69 {
70 private static final Logger LOG = Log.getLogger(Server.class);
71
72 private final AttributesMap _attributes = new AttributesMap();
73 private final ThreadPool _threadPool;
74 private final List<Connector> _connectors = new CopyOnWriteArrayList<>();
75 private SessionIdManager _sessionIdManager;
76 private boolean _sendServerVersion = true;
77 private boolean _sendDateHeader = false;
78 private boolean _stopAtShutdown;
79 private boolean _dumpAfterStart=false;
80 private boolean _dumpBeforeStop=false;
81
82
83
84 public Server()
85 {
86 this((ThreadPool)null);
87 }
88
89
90
91
92
93 public Server(@Name("port")int port)
94 {
95 this((ThreadPool)null);
96 ServerConnector connector=new ServerConnector(this);
97 connector.setPort(port);
98 setConnectors(new Connector[]{connector});
99 }
100
101
102
103
104
105 public Server(@Name("address")InetSocketAddress addr)
106 {
107 this((ThreadPool)null);
108 ServerConnector connector=new ServerConnector(this);
109 connector.setHost(addr.getHostName());
110 connector.setPort(addr.getPort());
111 setConnectors(new Connector[]{connector});
112 }
113
114
115
116
117 public Server(@Name("threadpool") ThreadPool pool)
118 {
119 _threadPool=pool!=null?pool:new QueuedThreadPool();
120 addBean(_threadPool);
121 setServer(this);
122 }
123
124
125
126 @ManagedAttribute("version of this server")
127 public static String getVersion()
128 {
129 return Jetty.VERSION;
130 }
131
132
133 public boolean getStopAtShutdown()
134 {
135 return _stopAtShutdown;
136 }
137
138
139 public void setStopAtShutdown(boolean stop)
140 {
141
142 if (stop)
143 {
144
145 if (!_stopAtShutdown)
146 {
147
148 if (isStarted())
149 ShutdownThread.register(this);
150 }
151 }
152 else
153 ShutdownThread.deregister(this);
154
155 _stopAtShutdown=stop;
156 }
157
158
159
160
161
162 @ManagedAttribute("connectors for this server")
163 public Connector[] getConnectors()
164 {
165 List<Connector> connectors = new ArrayList<>(_connectors);
166 return connectors.toArray(new Connector[connectors.size()]);
167 }
168
169
170 public void addConnector(Connector connector)
171 {
172 if (connector.getServer() != this)
173 throw new IllegalArgumentException("Connector " + connector +
174 " cannot be shared among server " + connector.getServer() + " and server " + this);
175 if (_connectors.add(connector))
176 addBean(connector);
177 }
178
179
180
181
182
183
184
185 public void removeConnector(Connector connector)
186 {
187 if (_connectors.remove(connector))
188 removeBean(connector);
189 }
190
191
192
193
194
195
196 public void setConnectors(Connector[] connectors)
197 {
198 if (connectors != null)
199 {
200 for (Connector connector : connectors)
201 {
202 if (connector.getServer() != this)
203 throw new IllegalArgumentException("Connector " + connector +
204 " cannot be shared among server " + connector.getServer() + " and server " + this);
205 }
206 }
207
208 Connector[] oldConnectors = getConnectors();
209 updateBeans(oldConnectors, connectors);
210 _connectors.removeAll(Arrays.asList(oldConnectors));
211 if (connectors != null)
212 _connectors.addAll(Arrays.asList(connectors));
213 }
214
215
216
217
218
219 @ManagedAttribute("the server thread pool")
220 public ThreadPool getThreadPool()
221 {
222 return _threadPool;
223 }
224
225
226
227
228 @ManagedAttribute("dump state to stderr after start")
229 public boolean isDumpAfterStart()
230 {
231 return _dumpAfterStart;
232 }
233
234
235
236
237 public void setDumpAfterStart(boolean dumpAfterStart)
238 {
239 _dumpAfterStart = dumpAfterStart;
240 }
241
242
243
244
245 @ManagedAttribute("dump state to stderr before stop")
246 public boolean isDumpBeforeStop()
247 {
248 return _dumpBeforeStop;
249 }
250
251
252
253
254 public void setDumpBeforeStop(boolean dumpBeforeStop)
255 {
256 _dumpBeforeStop = dumpBeforeStop;
257 }
258
259
260
261
262 @Override
263 protected void doStart() throws Exception
264 {
265 if (getStopAtShutdown())
266 ShutdownThread.register(this);
267
268 LOG.info("jetty-"+getVersion());
269 HttpGenerator.setServerVersion(getVersion());
270 MultiException mex=new MultiException();
271
272 try
273 {
274 super.doStart();
275 }
276 catch(Throwable e)
277 {
278 mex.add(e);
279 }
280
281 if (mex.size()==0)
282 {
283 for (Connector _connector : _connectors)
284 {
285 try
286 {
287 _connector.start();
288 }
289 catch (Throwable e)
290 {
291 mex.add(e);
292 }
293 }
294 }
295
296 if (isDumpAfterStart())
297 dumpStdErr();
298
299 mex.ifExceptionThrow();
300 }
301
302 @Override
303 protected void start(LifeCycle l) throws Exception
304 {
305
306 if (!(l instanceof Connector))
307 super.start(l);
308 }
309
310
311 @Override
312 protected void doStop() throws Exception
313 {
314 if (isDumpBeforeStop())
315 dumpStdErr();
316
317 MultiException mex=new MultiException();
318
319
320 List<Future<Void>> futures = new ArrayList<>();
321
322
323 for (Connector connector : _connectors)
324 futures.add(connector.shutdown((Void)null));
325
326
327 Handler[] contexts = getChildHandlersByClass(Graceful.class);
328 for (Handler context : contexts)
329 futures.add(((Graceful)context).shutdown((Void)null));
330
331
332 long stopTimeout = getStopTimeout();
333 if (stopTimeout>0)
334 {
335 long stop_by=System.currentTimeMillis()+stopTimeout;
336 LOG.info("Graceful shutdown {} by ",this,new Date(stop_by));
337
338
339 for (Future<Void> future: futures)
340 {
341 try
342 {
343 if (!future.isDone())
344 future.get(Math.max(1L,stop_by-System.currentTimeMillis()),TimeUnit.MILLISECONDS);
345 }
346 catch (Exception e)
347 {
348 mex.add(e.getCause());
349 }
350 }
351 }
352
353
354 for (Future<Void> future: futures)
355 if (!future.isDone())
356 future.cancel(true);
357
358
359 for (Connector connector : _connectors)
360 {
361 try
362 {
363 connector.stop();
364 }
365 catch (Throwable e)
366 {
367 mex.add(e);
368 }
369 }
370
371
372 try
373 {
374 super.doStop();
375 }
376 catch (Throwable e)
377 {
378 mex.add(e);
379 }
380
381 if (getStopAtShutdown())
382 ShutdownThread.deregister(this);
383
384 mex.ifExceptionThrow();
385
386 }
387
388
389
390
391
392
393
394 public void handle(HttpChannel<?> connection) throws IOException, ServletException
395 {
396 final String target=connection.getRequest().getPathInfo();
397 final Request request=connection.getRequest();
398 final Response response=connection.getResponse();
399
400 if (LOG.isDebugEnabled())
401 LOG.debug("REQUEST "+target+" on "+connection);
402
403 if ("*".equals(target))
404 {
405 handleOptions(request,response);
406 if (!request.isHandled())
407 handle(target, request, request, response);
408 }
409 else
410 handle(target, request, request, response);
411
412 if (LOG.isDebugEnabled())
413 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus()+" handled="+request.isHandled());
414 }
415
416
417
418
419 protected void handleOptions(Request request,Response response) throws IOException
420 {
421 if (!HttpMethod.OPTIONS.is(request.getMethod()))
422 response.sendError(HttpStatus.BAD_REQUEST_400);
423 request.setHandled(true);
424 response.setStatus(200);
425 response.getHttpFields().put(HttpHeader.ALLOW,"GET,POST,HEAD,OPTIONS");
426 response.setContentLength(0);
427 response.complete();
428 }
429
430
431
432
433
434
435
436 public void handleAsync(HttpChannel<?> connection) throws IOException, ServletException
437 {
438 final HttpChannelState async = connection.getRequest().getHttpChannelState();
439 final HttpChannelState.AsyncEventState state = async.getAsyncEventState();
440
441 final Request baseRequest=connection.getRequest();
442 final String path=state.getPath();
443
444 if (path!=null)
445 {
446
447 ServletContext context=state.getServletContext();
448 HttpURI uri = new HttpURI(context==null?path:URIUtil.addPaths(context.getContextPath(),path));
449 baseRequest.setUri(uri);
450 baseRequest.setRequestURI(null);
451 baseRequest.setPathInfo(baseRequest.getRequestURI());
452 if (uri.getQuery()!=null)
453 baseRequest.mergeQueryString(uri.getQuery());
454 }
455
456 final String target=baseRequest.getPathInfo();
457 final HttpServletRequest request=(HttpServletRequest)async.getRequest();
458 final HttpServletResponse response=(HttpServletResponse)async.getResponse();
459
460 if (LOG.isDebugEnabled())
461 {
462 LOG.debug("REQUEST "+target+" on "+connection);
463 handle(target, baseRequest, request, response);
464 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus());
465 }
466 else
467 handle(target, baseRequest, request, response);
468
469 }
470
471
472
473 public void join() throws InterruptedException
474 {
475 getThreadPool().join();
476 }
477
478
479
480
481
482 public SessionIdManager getSessionIdManager()
483 {
484 return _sessionIdManager;
485 }
486
487
488
489
490
491 public void setSessionIdManager(SessionIdManager sessionIdManager)
492 {
493 updateBean(_sessionIdManager,sessionIdManager);
494 _sessionIdManager=sessionIdManager;
495 }
496
497
498 public void setSendServerVersion (boolean sendServerVersion)
499 {
500 _sendServerVersion = sendServerVersion;
501 }
502
503
504 @ManagedAttribute("if true, include the server version in HTTP headers")
505 public boolean getSendServerVersion()
506 {
507 return _sendServerVersion;
508 }
509
510
511 public void setSendDateHeader(boolean sendDateHeader)
512 {
513 _sendDateHeader = sendDateHeader;
514 }
515
516
517 @ManagedAttribute("if true, include the date in HTTP headers")
518 public boolean getSendDateHeader()
519 {
520 return _sendDateHeader;
521 }
522
523
524
525
526
527 @Override
528 public void clearAttributes()
529 {
530 Enumeration names = _attributes.getAttributeNames();
531 while (names.hasMoreElements())
532 removeBean(_attributes.getAttribute((String)names.nextElement()));
533 _attributes.clearAttributes();
534 }
535
536
537
538
539
540 @Override
541 public Object getAttribute(String name)
542 {
543 return _attributes.getAttribute(name);
544 }
545
546
547
548
549
550 @Override
551 public Enumeration<String> getAttributeNames()
552 {
553 return AttributesMap.getAttributeNamesCopy(_attributes);
554 }
555
556
557
558
559
560 @Override
561 public void removeAttribute(String name)
562 {
563 Object bean=_attributes.getAttribute(name);
564 if (bean!=null)
565 removeBean(bean);
566 _attributes.removeAttribute(name);
567 }
568
569
570
571
572
573 @Override
574 public void setAttribute(String name, Object attribute)
575 {
576 addBean(attribute);
577 _attributes.setAttribute(name, attribute);
578 }
579
580
581 @Override
582 public String toString()
583 {
584 return this.getClass().getName()+"@"+Integer.toHexString(hashCode());
585 }
586
587 @Override
588 public void dump(Appendable out,String indent) throws IOException
589 {
590 dumpBeans(out,indent,Collections.singleton(new ClassLoaderDump(this.getClass().getClassLoader())));
591 }
592
593
594 public static void main(String...args) throws Exception
595 {
596 System.err.println(getVersion());
597 }
598 }