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.InetAddress;
23 import java.net.InetSocketAddress;
24 import java.net.URI;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.Date;
29 import java.util.Enumeration;
30 import java.util.List;
31 import java.util.concurrent.CopyOnWriteArrayList;
32 import java.util.concurrent.Future;
33 import java.util.concurrent.TimeUnit;
34
35 import javax.servlet.ServletContext;
36 import javax.servlet.ServletException;
37 import javax.servlet.http.HttpServletRequest;
38 import javax.servlet.http.HttpServletResponse;
39
40 import org.eclipse.jetty.http.DateGenerator;
41 import org.eclipse.jetty.http.HttpField;
42 import org.eclipse.jetty.http.HttpGenerator;
43 import org.eclipse.jetty.http.HttpHeader;
44 import org.eclipse.jetty.http.HttpMethod;
45 import org.eclipse.jetty.http.HttpStatus;
46 import org.eclipse.jetty.http.HttpURI;
47 import org.eclipse.jetty.server.handler.ContextHandler;
48 import org.eclipse.jetty.server.handler.HandlerWrapper;
49 import org.eclipse.jetty.server.handler.StatisticsHandler;
50 import org.eclipse.jetty.util.Attributes;
51 import org.eclipse.jetty.util.AttributesMap;
52 import org.eclipse.jetty.util.Jetty;
53 import org.eclipse.jetty.util.MultiException;
54 import org.eclipse.jetty.util.URIUtil;
55 import org.eclipse.jetty.util.Uptime;
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",Uptime.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()+" "+request.getMethod()+" "+target+" on "+connection);
489
490 if (HttpMethod.OPTIONS.is(request.getMethod()) || "*".equals(target))
491 {
492 if (!HttpMethod.OPTIONS.is(request.getMethod()))
493 response.sendError(HttpStatus.BAD_REQUEST_400);
494 handleOptions(request,response);
495 if (!request.isHandled())
496 handle(target, request, request, response);
497 }
498 else
499 handle(target, request, request, response);
500
501 if (LOG.isDebugEnabled())
502 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus()+" handled="+request.isHandled());
503 }
504
505
506
507
508 protected void handleOptions(Request request,Response response) throws IOException
509 {
510 }
511
512
513
514
515
516
517
518 public void handleAsync(HttpChannel<?> connection) throws IOException, ServletException
519 {
520 final HttpChannelState state = connection.getRequest().getHttpChannelState();
521 final AsyncContextEvent event = state.getAsyncContextEvent();
522
523 final Request baseRequest=connection.getRequest();
524 final String path=event.getPath();
525
526 if (path!=null)
527 {
528
529 ServletContext context=event.getServletContext();
530 HttpURI uri = new HttpURI(URIUtil.addPaths(context==null?null:context.getContextPath(), path));
531 baseRequest.setUri(uri);
532 baseRequest.setRequestURI(null);
533 baseRequest.setPathInfo(uri.getDecodedPath());
534 if (uri.getQuery()!=null)
535 baseRequest.mergeQueryParameters(uri.getQuery(), true);
536 }
537
538 final String target=baseRequest.getPathInfo();
539 final HttpServletRequest request=(HttpServletRequest)event.getSuppliedRequest();
540 final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse();
541
542 if (LOG.isDebugEnabled())
543 {
544 LOG.debug(request.getDispatcherType()+" "+request.getMethod()+" "+target+" on "+connection);
545 handle(target, baseRequest, request, response);
546 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus());
547 }
548 else
549 handle(target, baseRequest, request, response);
550
551 }
552
553
554 public void join() throws InterruptedException
555 {
556 getThreadPool().join();
557 }
558
559
560
561
562
563 public SessionIdManager getSessionIdManager()
564 {
565 return _sessionIdManager;
566 }
567
568
569
570
571
572 public void setSessionIdManager(SessionIdManager sessionIdManager)
573 {
574 updateBean(_sessionIdManager,sessionIdManager);
575 _sessionIdManager=sessionIdManager;
576 }
577
578
579
580
581
582 @Override
583 public void clearAttributes()
584 {
585 Enumeration<String> names = _attributes.getAttributeNames();
586 while (names.hasMoreElements())
587 removeBean(_attributes.getAttribute(names.nextElement()));
588 _attributes.clearAttributes();
589 }
590
591
592
593
594
595 @Override
596 public Object getAttribute(String name)
597 {
598 return _attributes.getAttribute(name);
599 }
600
601
602
603
604
605 @Override
606 public Enumeration<String> getAttributeNames()
607 {
608 return AttributesMap.getAttributeNamesCopy(_attributes);
609 }
610
611
612
613
614
615 @Override
616 public void removeAttribute(String name)
617 {
618 Object bean=_attributes.getAttribute(name);
619 if (bean!=null)
620 removeBean(bean);
621 _attributes.removeAttribute(name);
622 }
623
624
625
626
627
628 @Override
629 public void setAttribute(String name, Object attribute)
630 {
631 addBean(attribute);
632 _attributes.setAttribute(name, attribute);
633 }
634
635
636
637
638
639 @SuppressWarnings("resource")
640 public URI getURI()
641 {
642 NetworkConnector connector=null;
643 for (Connector c: _connectors)
644 {
645 if (c instanceof NetworkConnector)
646 {
647 connector=(NetworkConnector)c;
648 break;
649 }
650 }
651
652 if (connector==null)
653 return null;
654
655 ContextHandler context = getChildHandlerByClass(ContextHandler.class);
656
657 try
658 {
659 String scheme=connector.getDefaultConnectionFactory().getProtocol().startsWith("SSL-")?"https":"http";
660
661 String host=connector.getHost();
662 if (context!=null && context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
663 host=context.getVirtualHosts()[0];
664 if (host==null)
665 host=InetAddress.getLocalHost().getHostAddress();
666
667 String path=context==null?null:context.getContextPath();
668 if (path==null)
669 path="/";
670 return new URI(scheme,null,host,connector.getLocalPort(),path,null,null);
671 }
672 catch(Exception e)
673 {
674 LOG.warn(e);
675 return null;
676 }
677 }
678
679
680 @Override
681 public String toString()
682 {
683 return this.getClass().getName()+"@"+Integer.toHexString(hashCode());
684 }
685
686
687 @Override
688 public void dump(Appendable out,String indent) throws IOException
689 {
690 dumpBeans(out,indent,Collections.singleton(new ClassLoaderDump(this.getClass().getClassLoader())));
691 }
692
693
694 public static void main(String...args) throws Exception
695 {
696 System.err.println(getVersion());
697 }
698
699
700
701 private static class DateField
702 {
703 final long _seconds;
704 final HttpField _dateField;
705 public DateField(long seconds, HttpField dateField)
706 {
707 super();
708 _seconds = seconds;
709 _dateField = dateField;
710 }
711
712 }
713 }