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