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