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.TimerTask;
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.HttpField;
41 import org.eclipse.jetty.http.HttpFields;
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.DateCache;
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.annotation.ManagedAttribute;
56 import org.eclipse.jetty.util.annotation.ManagedObject;
57 import org.eclipse.jetty.util.annotation.Name;
58 import org.eclipse.jetty.util.component.Graceful;
59 import org.eclipse.jetty.util.component.LifeCycle;
60 import org.eclipse.jetty.util.log.Log;
61 import org.eclipse.jetty.util.log.Logger;
62 import org.eclipse.jetty.util.thread.QueuedThreadPool;
63 import org.eclipse.jetty.util.thread.ShutdownThread;
64 import org.eclipse.jetty.util.thread.ThreadPool;
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 private HttpField _dateField;
86
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
268 public HttpField getDateField()
269 {
270 return _dateField;
271 }
272
273
274 @Override
275 protected void doStart() throws Exception
276 {
277 if (getStopAtShutdown())
278 {
279 ShutdownThread.register(this);
280 }
281
282 ShutdownMonitor.getInstance().start();
283
284 LOG.info("jetty-"+getVersion());
285 HttpGenerator.setServerVersion(getVersion());
286 MultiException mex=new MultiException();
287
288 try
289 {
290 super.doStart();
291 }
292 catch(Throwable e)
293 {
294 mex.add(e);
295 }
296
297 if (mex.size()==0)
298 {
299 for (Connector _connector : _connectors)
300 {
301 try
302 {
303 _connector.start();
304 }
305 catch (Throwable e)
306 {
307 mex.add(e);
308 }
309 }
310 }
311
312 if (isDumpAfterStart())
313 dumpStdErr();
314
315
316
317 final HttpFields.DateGenerator date = new HttpFields.DateGenerator();
318 long now=System.currentTimeMillis();
319 long tick=1000*((now/1000)+1)-now;
320 _dateField=new HttpField.CachedHttpField(HttpHeader.DATE,date.formatDate(now));
321 DateCache.getTimer().scheduleAtFixedRate(new TimerTask()
322 {
323 @Override
324 public void run()
325 {
326 long now=System.currentTimeMillis();
327 _dateField=new HttpField.CachedHttpField(HttpHeader.DATE,date.formatDate(now));
328 if (!isRunning())
329 this.cancel();
330 }
331 },tick,1000);
332
333
334 mex.ifExceptionThrow();
335 }
336
337 @Override
338 protected void start(LifeCycle l) throws Exception
339 {
340
341 if (!(l instanceof Connector))
342 super.start(l);
343 }
344
345
346 @Override
347 protected void doStop() throws Exception
348 {
349 if (isDumpBeforeStop())
350 dumpStdErr();
351
352 MultiException mex=new MultiException();
353
354
355 List<Future<Void>> futures = new ArrayList<>();
356
357
358 for (Connector connector : _connectors)
359 futures.add(connector.shutdown());
360
361
362 Handler[] contexts = getChildHandlersByClass(Graceful.class);
363 for (Handler context : contexts)
364 futures.add(((Graceful)context).shutdown());
365
366
367 long stopTimeout = getStopTimeout();
368 if (stopTimeout>0)
369 {
370 long stop_by=System.currentTimeMillis()+stopTimeout;
371 LOG.debug("Graceful shutdown {} by ",this,new Date(stop_by));
372
373
374 for (Future<Void> future: futures)
375 {
376 try
377 {
378 if (!future.isDone())
379 future.get(Math.max(1L,stop_by-System.currentTimeMillis()),TimeUnit.MILLISECONDS);
380 }
381 catch (Exception e)
382 {
383 mex.add(e.getCause());
384 }
385 }
386 }
387
388
389 for (Future<Void> future: futures)
390 if (!future.isDone())
391 future.cancel(true);
392
393
394 for (Connector connector : _connectors)
395 {
396 try
397 {
398 connector.stop();
399 }
400 catch (Throwable e)
401 {
402 mex.add(e);
403 }
404 }
405
406
407 try
408 {
409 super.doStop();
410 }
411 catch (Throwable e)
412 {
413 mex.add(e);
414 }
415
416 if (getStopAtShutdown())
417 ShutdownThread.deregister(this);
418
419 mex.ifExceptionThrow();
420
421 }
422
423
424
425
426
427
428
429 public void handle(HttpChannel<?> connection) throws IOException, ServletException
430 {
431 final String target=connection.getRequest().getPathInfo();
432 final Request request=connection.getRequest();
433 final Response response=connection.getResponse();
434
435 if (LOG.isDebugEnabled())
436 LOG.debug(request.getDispatcherType()+" "+target+" on "+connection);
437
438 if ("*".equals(target))
439 {
440 handleOptions(request,response);
441 if (!request.isHandled())
442 handle(target, request, request, response);
443 }
444 else
445 handle(target, request, request, response);
446
447 if (LOG.isDebugEnabled())
448 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus()+" handled="+request.isHandled());
449 }
450
451
452
453
454 protected void handleOptions(Request request,Response response) throws IOException
455 {
456 if (!HttpMethod.OPTIONS.is(request.getMethod()))
457 response.sendError(HttpStatus.BAD_REQUEST_400);
458 request.setHandled(true);
459 response.setStatus(200);
460 response.getHttpFields().put(HttpHeader.ALLOW,"GET,POST,HEAD,OPTIONS");
461 response.setContentLength(0);
462 response.closeOutput();
463 }
464
465
466
467
468
469
470
471 public void handleAsync(HttpChannel<?> connection) throws IOException, ServletException
472 {
473 final HttpChannelState state = connection.getRequest().getHttpChannelState();
474 final AsyncContextEvent event = state.getAsyncContextEvent();
475
476 final Request baseRequest=connection.getRequest();
477 final String path=event.getPath();
478
479 if (path!=null)
480 {
481
482 ServletContext context=event.getServletContext();
483 HttpURI uri = new HttpURI(context==null?path:URIUtil.addPaths(context.getContextPath(),path));
484 baseRequest.setUri(uri);
485 baseRequest.setRequestURI(null);
486 baseRequest.setPathInfo(baseRequest.getRequestURI());
487 if (uri.getQuery()!=null)
488 baseRequest.mergeQueryString(uri.getQuery());
489 }
490
491 final String target=baseRequest.getPathInfo();
492 final HttpServletRequest request=(HttpServletRequest)event.getSuppliedRequest();
493 final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse();
494
495 if (LOG.isDebugEnabled())
496 {
497 LOG.debug(request.getDispatcherType()+" "+target+" on "+connection);
498 handle(target, baseRequest, request, response);
499 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus());
500 }
501 else
502 handle(target, baseRequest, request, response);
503
504 }
505
506
507 public void join() throws InterruptedException
508 {
509 getThreadPool().join();
510 }
511
512
513
514
515
516 public SessionIdManager getSessionIdManager()
517 {
518 return _sessionIdManager;
519 }
520
521
522
523
524
525 public void setSessionIdManager(SessionIdManager sessionIdManager)
526 {
527 updateBean(_sessionIdManager,sessionIdManager);
528 _sessionIdManager=sessionIdManager;
529 }
530
531
532
533
534
535 @Override
536 public void clearAttributes()
537 {
538 Enumeration names = _attributes.getAttributeNames();
539 while (names.hasMoreElements())
540 removeBean(_attributes.getAttribute((String)names.nextElement()));
541 _attributes.clearAttributes();
542 }
543
544
545
546
547
548 @Override
549 public Object getAttribute(String name)
550 {
551 return _attributes.getAttribute(name);
552 }
553
554
555
556
557
558 @Override
559 public Enumeration<String> getAttributeNames()
560 {
561 return AttributesMap.getAttributeNamesCopy(_attributes);
562 }
563
564
565
566
567
568 @Override
569 public void removeAttribute(String name)
570 {
571 Object bean=_attributes.getAttribute(name);
572 if (bean!=null)
573 removeBean(bean);
574 _attributes.removeAttribute(name);
575 }
576
577
578
579
580
581 @Override
582 public void setAttribute(String name, Object attribute)
583 {
584 addBean(attribute);
585 _attributes.setAttribute(name, attribute);
586 }
587
588
589
590
591
592 public URI getURI()
593 {
594 NetworkConnector connector=null;
595 for (Connector c: _connectors)
596 {
597 if (c instanceof NetworkConnector)
598 {
599 connector=(NetworkConnector)c;
600 break;
601 }
602 }
603
604 if (connector==null)
605 return null;
606
607 ContextHandler context = getChildHandlerByClass(ContextHandler.class);
608
609 try
610 {
611 String scheme=connector.getDefaultConnectionFactory().getProtocol().startsWith("SSL-")?"https":"http";
612
613 String host=connector.getHost();
614 if (context!=null && context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
615 host=context.getVirtualHosts()[0];
616 if (host==null)
617 host=InetAddress.getLocalHost().getHostAddress();
618
619 String path=context==null?null:context.getContextPath();
620 if (path==null)
621 path="/";
622 return new URI(scheme,null,host,connector.getLocalPort(),path,null,null);
623 }
624 catch(Exception e)
625 {
626 LOG.warn(e);
627 return null;
628 }
629 }
630
631
632 @Override
633 public String toString()
634 {
635 return this.getClass().getName()+"@"+Integer.toHexString(hashCode());
636 }
637
638 @Override
639 public void dump(Appendable out,String indent) throws IOException
640 {
641 dumpBeans(out,indent,Collections.singleton(new ClassLoaderDump(this.getClass().getClassLoader())));
642 }
643
644
645 public static void main(String...args) throws Exception
646 {
647 System.err.println(getVersion());
648 }
649 }