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.lang.management.ManagementFactory;
23 import java.net.InetAddress;
24 import java.net.InetSocketAddress;
25 import java.net.URI;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collections;
29 import java.util.Date;
30 import java.util.Enumeration;
31 import java.util.List;
32 import java.util.concurrent.CopyOnWriteArrayList;
33 import java.util.concurrent.Future;
34 import java.util.concurrent.TimeUnit;
35
36 import javax.servlet.ServletContext;
37 import javax.servlet.ServletException;
38 import javax.servlet.http.HttpServletRequest;
39 import javax.servlet.http.HttpServletResponse;
40
41 import org.eclipse.jetty.http.DateGenerator;
42 import org.eclipse.jetty.http.HttpField;
43 import org.eclipse.jetty.http.HttpGenerator;
44 import org.eclipse.jetty.http.HttpHeader;
45 import org.eclipse.jetty.http.HttpMethod;
46 import org.eclipse.jetty.http.HttpStatus;
47 import org.eclipse.jetty.http.HttpURI;
48 import org.eclipse.jetty.server.handler.ContextHandler;
49 import org.eclipse.jetty.server.handler.HandlerWrapper;
50 import org.eclipse.jetty.server.handler.StatisticsHandler;
51 import org.eclipse.jetty.util.Attributes;
52 import org.eclipse.jetty.util.AttributesMap;
53 import org.eclipse.jetty.util.Jetty;
54 import org.eclipse.jetty.util.MultiException;
55 import org.eclipse.jetty.util.URIUtil;
56 import org.eclipse.jetty.util.annotation.ManagedAttribute;
57 import org.eclipse.jetty.util.annotation.ManagedObject;
58 import org.eclipse.jetty.util.annotation.Name;
59 import org.eclipse.jetty.util.component.Graceful;
60 import org.eclipse.jetty.util.component.LifeCycle;
61 import org.eclipse.jetty.util.log.Log;
62 import org.eclipse.jetty.util.log.Logger;
63 import org.eclipse.jetty.util.thread.QueuedThreadPool;
64 import org.eclipse.jetty.util.thread.ShutdownThread;
65 import org.eclipse.jetty.util.thread.ThreadPool;
66 import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
67
68
69
70
71
72
73
74
75 @ManagedObject(value="Jetty HTTP Servlet server")
76 public class Server extends HandlerWrapper implements Attributes
77 {
78 private static final Logger LOG = Log.getLogger(Server.class);
79
80 private final AttributesMap _attributes = new AttributesMap();
81 private final ThreadPool _threadPool;
82 private final List<Connector> _connectors = new CopyOnWriteArrayList<>();
83 private SessionIdManager _sessionIdManager;
84 private boolean _stopAtShutdown;
85 private boolean _dumpAfterStart=false;
86 private boolean _dumpBeforeStop=false;
87
88 private volatile DateField _dateField;
89
90
91 public Server()
92 {
93 this((ThreadPool)null);
94 }
95
96
97
98
99
100
101
102 public Server(@Name("port")int port)
103 {
104 this((ThreadPool)null);
105 ServerConnector connector=new ServerConnector(this);
106 connector.setPort(port);
107 setConnectors(new Connector[]{connector});
108 }
109
110
111
112
113
114 public Server(@Name("address")InetSocketAddress addr)
115 {
116 this((ThreadPool)null);
117 ServerConnector connector=new ServerConnector(this);
118 connector.setHost(addr.getHostName());
119 connector.setPort(addr.getPort());
120 setConnectors(new Connector[]{connector});
121 }
122
123
124
125
126 public Server(@Name("threadpool") ThreadPool pool)
127 {
128 _threadPool=pool!=null?pool:new QueuedThreadPool();
129 addBean(_threadPool);
130 setServer(this);
131 }
132
133
134
135 @ManagedAttribute("version of this server")
136 public static String getVersion()
137 {
138 return Jetty.VERSION;
139 }
140
141
142 public boolean getStopAtShutdown()
143 {
144 return _stopAtShutdown;
145 }
146
147
148
149
150
151
152
153
154
155 @Override
156 public void setStopTimeout(long stopTimeout)
157 {
158 super.setStopTimeout(stopTimeout);
159 }
160
161
162
163
164
165
166
167
168 public void setStopAtShutdown(boolean stop)
169 {
170
171 if (stop)
172 {
173
174 if (!_stopAtShutdown)
175 {
176
177 if (isStarted())
178 ShutdownThread.register(this);
179 }
180 }
181 else
182 ShutdownThread.deregister(this);
183
184 _stopAtShutdown=stop;
185 }
186
187
188
189
190
191 @ManagedAttribute(value="connectors for this server", readonly=true)
192 public Connector[] getConnectors()
193 {
194 List<Connector> connectors = new ArrayList<>(_connectors);
195 return connectors.toArray(new Connector[connectors.size()]);
196 }
197
198
199 public void addConnector(Connector connector)
200 {
201 if (connector.getServer() != this)
202 throw new IllegalArgumentException("Connector " + connector +
203 " cannot be shared among server " + connector.getServer() + " and server " + this);
204 if (_connectors.add(connector))
205 addBean(connector);
206 }
207
208
209
210
211
212
213
214 public void removeConnector(Connector connector)
215 {
216 if (_connectors.remove(connector))
217 removeBean(connector);
218 }
219
220
221
222
223
224
225 public void setConnectors(Connector[] connectors)
226 {
227 if (connectors != null)
228 {
229 for (Connector connector : connectors)
230 {
231 if (connector.getServer() != this)
232 throw new IllegalArgumentException("Connector " + connector +
233 " cannot be shared among server " + connector.getServer() + " and server " + this);
234 }
235 }
236
237 Connector[] oldConnectors = getConnectors();
238 updateBeans(oldConnectors, connectors);
239 _connectors.removeAll(Arrays.asList(oldConnectors));
240 if (connectors != null)
241 _connectors.addAll(Arrays.asList(connectors));
242 }
243
244
245
246
247
248 @ManagedAttribute("the server thread pool")
249 public ThreadPool getThreadPool()
250 {
251 return _threadPool;
252 }
253
254
255
256
257 @ManagedAttribute("dump state to stderr after start")
258 public boolean isDumpAfterStart()
259 {
260 return _dumpAfterStart;
261 }
262
263
264
265
266 public void setDumpAfterStart(boolean dumpAfterStart)
267 {
268 _dumpAfterStart = dumpAfterStart;
269 }
270
271
272
273
274 @ManagedAttribute("dump state to stderr before stop")
275 public boolean isDumpBeforeStop()
276 {
277 return _dumpBeforeStop;
278 }
279
280
281
282
283 public void setDumpBeforeStop(boolean dumpBeforeStop)
284 {
285 _dumpBeforeStop = dumpBeforeStop;
286 }
287
288
289 public HttpField getDateField()
290 {
291 long now=System.currentTimeMillis();
292 long seconds = now/1000;
293 DateField df = _dateField;
294
295 if (df==null || df._seconds!=seconds)
296 {
297 synchronized (this)
298 {
299 df = _dateField;
300 if (df==null || df._seconds!=seconds)
301 {
302 HttpField field=new HttpGenerator.CachedHttpField(HttpHeader.DATE,DateGenerator.formatDate(now));
303 _dateField=new DateField(seconds,field);
304 return field;
305 }
306 }
307 }
308 return df._dateField;
309 }
310
311
312 @Override
313 protected void doStart() throws Exception
314 {
315
316
317 if (getStopAtShutdown())
318 ShutdownThread.register(this);
319
320
321
322 ShutdownMonitor.register(this);
323
324
325 ShutdownMonitor.getInstance().start();
326
327 LOG.info("jetty-" + getVersion());
328 HttpGenerator.setJettyVersion(HttpConfiguration.SERVER_VERSION);
329 MultiException mex=new MultiException();
330
331
332 SizedThreadPool pool = getBean(SizedThreadPool.class);
333 int max=pool==null?-1:pool.getMaxThreads();
334 int selectors=0;
335 int acceptors=0;
336 if (mex.size()==0)
337 {
338 for (Connector connector : _connectors)
339 {
340 if (connector instanceof AbstractConnector)
341 acceptors+=((AbstractConnector)connector).getAcceptors();
342
343 if (connector instanceof ServerConnector)
344 selectors+=((ServerConnector)connector).getSelectorManager().getSelectorCount();
345 }
346 }
347
348 int needed=1+selectors+acceptors;
349 if (max>0 && needed>max)
350 throw new IllegalStateException(String.format("Insufficient threads: max=%d < needed(acceptors=%d + selectors=%d + request=1)",max,acceptors,selectors));
351
352 try
353 {
354 super.doStart();
355 }
356 catch(Throwable e)
357 {
358 mex.add(e);
359 }
360
361
362 for (Connector connector : _connectors)
363 {
364 try
365 {
366 connector.start();
367 }
368 catch(Throwable e)
369 {
370 mex.add(e);
371 }
372 }
373
374 if (isDumpAfterStart())
375 dumpStdErr();
376
377 mex.ifExceptionThrow();
378
379 LOG.info(String.format("Started @%dms",ManagementFactory.getRuntimeMXBean().getUptime()));
380 }
381
382 @Override
383 protected void start(LifeCycle l) throws Exception
384 {
385
386 if (!(l instanceof Connector))
387 super.start(l);
388 }
389
390
391 @Override
392 protected void doStop() throws Exception
393 {
394 if (isDumpBeforeStop())
395 dumpStdErr();
396
397 MultiException mex=new MultiException();
398
399
400 List<Future<Void>> futures = new ArrayList<>();
401
402
403 for (Connector connector : _connectors)
404 futures.add(connector.shutdown());
405
406
407
408 Handler[] gracefuls = getChildHandlersByClass(Graceful.class);
409 for (Handler graceful : gracefuls)
410 futures.add(((Graceful)graceful).shutdown());
411
412
413 long stopTimeout = getStopTimeout();
414 if (stopTimeout>0)
415 {
416 long stop_by=System.currentTimeMillis()+stopTimeout;
417 if (LOG.isDebugEnabled())
418 LOG.debug("Graceful shutdown {} by ",this,new Date(stop_by));
419
420
421 for (Future<Void> future: futures)
422 {
423 try
424 {
425 if (!future.isDone())
426 future.get(Math.max(1L,stop_by-System.currentTimeMillis()),TimeUnit.MILLISECONDS);
427 }
428 catch (Exception e)
429 {
430 mex.add(e);
431 }
432 }
433 }
434
435
436 for (Future<Void> future: futures)
437 if (!future.isDone())
438 future.cancel(true);
439
440
441 for (Connector connector : _connectors)
442 {
443 try
444 {
445 connector.stop();
446 }
447 catch (Throwable e)
448 {
449 mex.add(e);
450 }
451 }
452
453
454 try
455 {
456 super.doStop();
457 }
458 catch (Throwable e)
459 {
460 mex.add(e);
461 }
462
463 if (getStopAtShutdown())
464 ShutdownThread.deregister(this);
465
466
467
468 ShutdownMonitor.deregister(this);
469
470
471 mex.ifExceptionThrow();
472
473 }
474
475
476
477
478
479
480
481 public void handle(HttpChannel<?> connection) throws IOException, ServletException
482 {
483 final String target=connection.getRequest().getPathInfo();
484 final Request request=connection.getRequest();
485 final Response response=connection.getResponse();
486
487 if (LOG.isDebugEnabled())
488 LOG.debug(request.getDispatcherType()+" "+target+" on "+connection);
489
490 if ("*".equals(target))
491 {
492 handleOptions(request,response);
493 if (!request.isHandled())
494 handle(target, request, request, response);
495 }
496 else
497 handle(target, request, request, response);
498
499 if (LOG.isDebugEnabled())
500 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus()+" handled="+request.isHandled());
501 }
502
503
504
505
506 protected void handleOptions(Request request,Response response) throws IOException
507 {
508 if (!HttpMethod.OPTIONS.is(request.getMethod()))
509 response.sendError(HttpStatus.BAD_REQUEST_400);
510 request.setHandled(true);
511 response.setStatus(200);
512 response.setContentLength(0);
513 response.closeOutput();
514 }
515
516
517
518
519
520
521
522 public void handleAsync(HttpChannel<?> connection) throws IOException, ServletException
523 {
524 final HttpChannelState state = connection.getRequest().getHttpChannelState();
525 final AsyncContextEvent event = state.getAsyncContextEvent();
526
527 final Request baseRequest=connection.getRequest();
528 final String path=event.getPath();
529
530 if (path!=null)
531 {
532
533 ServletContext context=event.getServletContext();
534 HttpURI uri = new HttpURI(URIUtil.addPaths(context==null?null:context.getContextPath(), path));
535 baseRequest.setUri(uri);
536 baseRequest.setRequestURI(null);
537 baseRequest.setPathInfo(uri.getDecodedPath());
538 if (uri.getQuery()!=null)
539 baseRequest.mergeQueryParameters(uri.getQuery(), true);
540 }
541
542 final String target=baseRequest.getPathInfo();
543 final HttpServletRequest request=(HttpServletRequest)event.getSuppliedRequest();
544 final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse();
545
546 if (LOG.isDebugEnabled())
547 {
548 LOG.debug(request.getDispatcherType()+" "+target+" on "+connection);
549 handle(target, baseRequest, request, response);
550 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus());
551 }
552 else
553 handle(target, baseRequest, request, response);
554
555 }
556
557
558 public void join() throws InterruptedException
559 {
560 getThreadPool().join();
561 }
562
563
564
565
566
567 public SessionIdManager getSessionIdManager()
568 {
569 return _sessionIdManager;
570 }
571
572
573
574
575
576 public void setSessionIdManager(SessionIdManager sessionIdManager)
577 {
578 updateBean(_sessionIdManager,sessionIdManager);
579 _sessionIdManager=sessionIdManager;
580 }
581
582
583
584
585
586 @Override
587 public void clearAttributes()
588 {
589 Enumeration<String> names = _attributes.getAttributeNames();
590 while (names.hasMoreElements())
591 removeBean(_attributes.getAttribute(names.nextElement()));
592 _attributes.clearAttributes();
593 }
594
595
596
597
598
599 @Override
600 public Object getAttribute(String name)
601 {
602 return _attributes.getAttribute(name);
603 }
604
605
606
607
608
609 @Override
610 public Enumeration<String> getAttributeNames()
611 {
612 return AttributesMap.getAttributeNamesCopy(_attributes);
613 }
614
615
616
617
618
619 @Override
620 public void removeAttribute(String name)
621 {
622 Object bean=_attributes.getAttribute(name);
623 if (bean!=null)
624 removeBean(bean);
625 _attributes.removeAttribute(name);
626 }
627
628
629
630
631
632 @Override
633 public void setAttribute(String name, Object attribute)
634 {
635 addBean(attribute);
636 _attributes.setAttribute(name, attribute);
637 }
638
639
640
641
642
643 @SuppressWarnings("resource")
644 public URI getURI()
645 {
646 NetworkConnector connector=null;
647 for (Connector c: _connectors)
648 {
649 if (c instanceof NetworkConnector)
650 {
651 connector=(NetworkConnector)c;
652 break;
653 }
654 }
655
656 if (connector==null)
657 return null;
658
659 ContextHandler context = getChildHandlerByClass(ContextHandler.class);
660
661 try
662 {
663 String scheme=connector.getDefaultConnectionFactory().getProtocol().startsWith("SSL-")?"https":"http";
664
665 String host=connector.getHost();
666 if (context!=null && context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
667 host=context.getVirtualHosts()[0];
668 if (host==null)
669 host=InetAddress.getLocalHost().getHostAddress();
670
671 String path=context==null?null:context.getContextPath();
672 if (path==null)
673 path="/";
674 return new URI(scheme,null,host,connector.getLocalPort(),path,null,null);
675 }
676 catch(Exception e)
677 {
678 LOG.warn(e);
679 return null;
680 }
681 }
682
683
684 @Override
685 public String toString()
686 {
687 return this.getClass().getName()+"@"+Integer.toHexString(hashCode());
688 }
689
690
691 @Override
692 public void dump(Appendable out,String indent) throws IOException
693 {
694 dumpBeans(out,indent,Collections.singleton(new ClassLoaderDump(this.getClass().getClassLoader())));
695 }
696
697
698 public static void main(String...args) throws Exception
699 {
700 System.err.println(getVersion());
701 }
702
703
704
705 private static class DateField
706 {
707 final long _seconds;
708 final HttpField _dateField;
709 public DateField(long seconds, HttpField dateField)
710 {
711 super();
712 _seconds = seconds;
713 _dateField = dateField;
714 }
715
716 }
717 }