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