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.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
350 @Override
351 protected void start(LifeCycle l) throws Exception
352 {
353
354 if (!(l instanceof Connector))
355 super.start(l);
356 }
357
358
359 @Override
360 protected void doStop() throws Exception
361 {
362 if (isDumpBeforeStop())
363 dumpStdErr();
364
365 MultiException mex=new MultiException();
366
367
368 List<Future<Void>> futures = new ArrayList<>();
369
370
371 for (Connector connector : _connectors)
372 futures.add(connector.shutdown());
373
374
375
376 Handler[] gracefuls = getChildHandlersByClass(Graceful.class);
377 for (Handler graceful : gracefuls)
378 futures.add(((Graceful)graceful).shutdown());
379
380
381 long stopTimeout = getStopTimeout();
382 if (stopTimeout>0)
383 {
384 long stop_by=System.currentTimeMillis()+stopTimeout;
385 LOG.debug("Graceful shutdown {} by ",this,new Date(stop_by));
386
387
388 for (Future<Void> future: futures)
389 {
390 try
391 {
392 if (!future.isDone())
393 future.get(Math.max(1L,stop_by-System.currentTimeMillis()),TimeUnit.MILLISECONDS);
394 }
395 catch (Exception e)
396 {
397 mex.add(e.getCause());
398 }
399 }
400 }
401
402
403 for (Future<Void> future: futures)
404 if (!future.isDone())
405 future.cancel(true);
406
407
408 for (Connector connector : _connectors)
409 {
410 try
411 {
412 connector.stop();
413 }
414 catch (Throwable e)
415 {
416 mex.add(e);
417 }
418 }
419
420
421 try
422 {
423 super.doStop();
424 }
425 catch (Throwable e)
426 {
427 mex.add(e);
428 }
429
430 if (getStopAtShutdown())
431 ShutdownThread.deregister(this);
432
433 mex.ifExceptionThrow();
434
435 }
436
437
438
439
440
441
442
443 public void handle(HttpChannel<?> connection) throws IOException, ServletException
444 {
445 final String target=connection.getRequest().getPathInfo();
446 final Request request=connection.getRequest();
447 final Response response=connection.getResponse();
448
449 if (LOG.isDebugEnabled())
450 LOG.debug(request.getDispatcherType()+" "+target+" on "+connection);
451
452 if ("*".equals(target))
453 {
454 handleOptions(request,response);
455 if (!request.isHandled())
456 handle(target, request, request, response);
457 }
458 else
459 handle(target, request, request, response);
460
461 if (LOG.isDebugEnabled())
462 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus()+" handled="+request.isHandled());
463 }
464
465
466
467
468 protected void handleOptions(Request request,Response response) throws IOException
469 {
470 if (!HttpMethod.OPTIONS.is(request.getMethod()))
471 response.sendError(HttpStatus.BAD_REQUEST_400);
472 request.setHandled(true);
473 response.setStatus(200);
474 response.getHttpFields().put(HttpHeader.ALLOW,"GET,POST,HEAD,OPTIONS");
475 response.setContentLength(0);
476 response.closeOutput();
477 }
478
479
480
481
482
483
484
485 public void handleAsync(HttpChannel<?> connection) throws IOException, ServletException
486 {
487 final HttpChannelState state = connection.getRequest().getHttpChannelState();
488 final AsyncContextEvent event = state.getAsyncContextEvent();
489
490 final Request baseRequest=connection.getRequest();
491 final String path=event.getPath();
492
493 if (path!=null)
494 {
495
496 ServletContext context=event.getServletContext();
497 HttpURI uri = new HttpURI(context==null?path:URIUtil.addPaths(context.getContextPath(),path));
498 baseRequest.setUri(uri);
499 baseRequest.setRequestURI(null);
500 baseRequest.setPathInfo(baseRequest.getRequestURI());
501 if (uri.getQuery()!=null)
502 baseRequest.mergeQueryString(uri.getQuery());
503 }
504
505 final String target=baseRequest.getPathInfo();
506 final HttpServletRequest request=(HttpServletRequest)event.getSuppliedRequest();
507 final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse();
508
509 if (LOG.isDebugEnabled())
510 {
511 LOG.debug(request.getDispatcherType()+" "+target+" on "+connection);
512 handle(target, baseRequest, request, response);
513 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus());
514 }
515 else
516 handle(target, baseRequest, request, response);
517
518 }
519
520
521 public void join() throws InterruptedException
522 {
523 getThreadPool().join();
524 }
525
526
527
528
529
530 public SessionIdManager getSessionIdManager()
531 {
532 return _sessionIdManager;
533 }
534
535
536
537
538
539 public void setSessionIdManager(SessionIdManager sessionIdManager)
540 {
541 updateBean(_sessionIdManager,sessionIdManager);
542 _sessionIdManager=sessionIdManager;
543 }
544
545
546
547
548
549 @Override
550 public void clearAttributes()
551 {
552 Enumeration<String> names = _attributes.getAttributeNames();
553 while (names.hasMoreElements())
554 removeBean(_attributes.getAttribute(names.nextElement()));
555 _attributes.clearAttributes();
556 }
557
558
559
560
561
562 @Override
563 public Object getAttribute(String name)
564 {
565 return _attributes.getAttribute(name);
566 }
567
568
569
570
571
572 @Override
573 public Enumeration<String> getAttributeNames()
574 {
575 return AttributesMap.getAttributeNamesCopy(_attributes);
576 }
577
578
579
580
581
582 @Override
583 public void removeAttribute(String name)
584 {
585 Object bean=_attributes.getAttribute(name);
586 if (bean!=null)
587 removeBean(bean);
588 _attributes.removeAttribute(name);
589 }
590
591
592
593
594
595 @Override
596 public void setAttribute(String name, Object attribute)
597 {
598 addBean(attribute);
599 _attributes.setAttribute(name, attribute);
600 }
601
602
603
604
605
606 public URI getURI()
607 {
608 NetworkConnector connector=null;
609 for (Connector c: _connectors)
610 {
611 if (c instanceof NetworkConnector)
612 {
613 connector=(NetworkConnector)c;
614 break;
615 }
616 }
617
618 if (connector==null)
619 return null;
620
621 ContextHandler context = getChildHandlerByClass(ContextHandler.class);
622
623 try
624 {
625 String scheme=connector.getDefaultConnectionFactory().getProtocol().startsWith("SSL-")?"https":"http";
626
627 String host=connector.getHost();
628 if (context!=null && context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
629 host=context.getVirtualHosts()[0];
630 if (host==null)
631 host=InetAddress.getLocalHost().getHostAddress();
632
633 String path=context==null?null:context.getContextPath();
634 if (path==null)
635 path="/";
636 return new URI(scheme,null,host,connector.getLocalPort(),path,null,null);
637 }
638 catch(Exception e)
639 {
640 LOG.warn(e);
641 return null;
642 }
643 }
644
645
646 @Override
647 public String toString()
648 {
649 return this.getClass().getName()+"@"+Integer.toHexString(hashCode());
650 }
651
652 @Override
653 public void dump(Appendable out,String indent) throws IOException
654 {
655 dumpBeans(out,indent,Collections.singleton(new ClassLoaderDump(this.getClass().getClassLoader())));
656 }
657
658
659 public static void main(String...args) throws Exception
660 {
661 System.err.println(getVersion());
662 }
663
664
665
666 private static class DateField
667 {
668 final long _seconds;
669 final HttpField _dateField;
670 public DateField(long seconds, HttpField dateField)
671 {
672 super();
673 _seconds = seconds;
674 _dateField = dateField;
675 }
676
677 }
678 }