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