2 // ========================================================================
3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.server;
21 import java.io.IOException;
22 import java.lang.management.ManagementFactory;
23 import java.net.InetAddress;
24 import java.net.InetSocketAddress;
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;
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;
66 /* ------------------------------------------------------------ */
67 /** Jetty HTTP Servlet Server.
68 * This class is the main class for the Jetty HTTP Servlet server.
69 * It aggregates Connectors (HTTP request receivers) and request Handlers.
70 * The server is itself a handler and a ThreadPool. Connectors use the ThreadPool methods
71 * to run jobs that will eventually call the handle method.
73 @ManagedObject(value="Jetty HTTP Servlet server")
74 public class Server extends HandlerWrapper implements Attributes
76 private static final Logger LOG = Log.getLogger(Server.class);
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;
86 private volatile DateField _dateField;
88 /* ------------------------------------------------------------ */
91 this((ThreadPool)null);
94 /* ------------------------------------------------------------ */
95 /** Convenience constructor
96 * Creates server and a {@link ServerConnector} at the passed port.
97 * @param port The port of a network HTTP connector (or 0 for a randomly allocated port).
98 * @see NetworkConnector#getLocalPort()
100 public Server(@Name("port")int port)
102 this((ThreadPool)null);
103 ServerConnector connector=new ServerConnector(this);
104 connector.setPort(port);
105 setConnectors(new Connector[]{connector});
108 /* ------------------------------------------------------------ */
109 /** Convenience constructor
110 * Creates server and a {@link ServerConnector} at the passed address.
112 public Server(@Name("address")InetSocketAddress addr)
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});
123 /* ------------------------------------------------------------ */
124 public Server(@Name("threadpool") ThreadPool pool)
126 _threadPool=pool!=null?pool:new QueuedThreadPool();
127 addBean(_threadPool);
132 /* ------------------------------------------------------------ */
133 @ManagedAttribute("version of this server")
134 public static String getVersion()
136 return Jetty.VERSION;
139 /* ------------------------------------------------------------ */
140 public boolean getStopAtShutdown()
142 return _stopAtShutdown;
145 /* ------------------------------------------------------------ */
146 public void setStopAtShutdown(boolean stop)
148 //if we now want to stop
151 //and we weren't stopping before
152 if (!_stopAtShutdown)
154 //only register to stop if we're already started (otherwise we'll do it in doStart())
156 ShutdownThread.register(this);
160 ShutdownThread.deregister(this);
162 _stopAtShutdown=stop;
165 /* ------------------------------------------------------------ */
167 * @return Returns the connectors.
169 @ManagedAttribute(value="connectors for this server", readonly=true)
170 public Connector[] getConnectors()
172 List<Connector> connectors = new ArrayList<>(_connectors);
173 return connectors.toArray(new Connector[connectors.size()]);
176 /* ------------------------------------------------------------ */
177 public void addConnector(Connector connector)
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))
186 /* ------------------------------------------------------------ */
188 * Convenience method which calls {@link #getConnectors()} and {@link #setConnectors(Connector[])} to
189 * remove a connector.
190 * @param connector The connector to remove.
192 public void removeConnector(Connector connector)
194 if (_connectors.remove(connector))
195 removeBean(connector);
198 /* ------------------------------------------------------------ */
199 /** Set the connectors for this server.
200 * Each connector has this server set as it's ThreadPool and its Handler.
201 * @param connectors The connectors to set.
203 public void setConnectors(Connector[] connectors)
205 if (connectors != null)
207 for (Connector connector : connectors)
209 if (connector.getServer() != this)
210 throw new IllegalArgumentException("Connector " + connector +
211 " cannot be shared among server " + connector.getServer() + " and server " + this);
215 Connector[] oldConnectors = getConnectors();
216 updateBeans(oldConnectors, connectors);
217 _connectors.removeAll(Arrays.asList(oldConnectors));
218 if (connectors != null)
219 _connectors.addAll(Arrays.asList(connectors));
222 /* ------------------------------------------------------------ */
224 * @return Returns the threadPool.
226 @ManagedAttribute("the server thread pool")
227 public ThreadPool getThreadPool()
233 * @return true if {@link #dumpStdErr()} is called after starting
235 @ManagedAttribute("dump state to stderr after start")
236 public boolean isDumpAfterStart()
238 return _dumpAfterStart;
242 * @param dumpAfterStart true if {@link #dumpStdErr()} is called after starting
244 public void setDumpAfterStart(boolean dumpAfterStart)
246 _dumpAfterStart = dumpAfterStart;
250 * @return true if {@link #dumpStdErr()} is called before stopping
252 @ManagedAttribute("dump state to stderr before stop")
253 public boolean isDumpBeforeStop()
255 return _dumpBeforeStop;
259 * @param dumpBeforeStop true if {@link #dumpStdErr()} is called before stopping
261 public void setDumpBeforeStop(boolean dumpBeforeStop)
263 _dumpBeforeStop = dumpBeforeStop;
266 /* ------------------------------------------------------------ */
267 public HttpField getDateField()
269 long now=System.currentTimeMillis();
270 long seconds = now/1000;
271 DateField df = _dateField;
273 if (df==null || df._seconds!=seconds)
275 synchronized (this) // Trade some contention for less garbage
278 if (df==null || df._seconds!=seconds)
280 HttpField field=new HttpGenerator.CachedHttpField(HttpHeader.DATE,DateGenerator.formatDate(now));
281 _dateField=new DateField(seconds,field);
286 return df._dateField;
289 /* ------------------------------------------------------------ */
291 protected void doStart() throws Exception
293 if (getStopAtShutdown())
295 ShutdownThread.register(this);
298 ShutdownMonitor.getInstance().start(); // initialize
300 LOG.info("jetty-" + getVersion());
301 HttpGenerator.setJettyVersion(HttpConfiguration.SERVER_VERSION);
302 MultiException mex=new MultiException();
304 // check size of thread pool
305 SizedThreadPool pool = getBean(SizedThreadPool.class);
306 int max=pool==null?-1:pool.getMaxThreads();
310 for (Connector connector : _connectors)
312 if (connector instanceof AbstractConnector)
313 needed+=((AbstractConnector)connector).getAcceptors();
314 if (connector instanceof ServerConnector)
315 needed+=((ServerConnector)connector).getSelectorManager().getSelectorCount();
319 if (max>0 && needed>max)
320 throw new IllegalStateException("Insufficient max threads in ThreadPool: max="+max+" < needed="+needed);
331 // start connectors last
332 for (Connector connector : _connectors)
344 if (isDumpAfterStart())
347 mex.ifExceptionThrow();
349 LOG.info(String.format("Started @%dms",ManagementFactory.getRuntimeMXBean().getUptime()));
353 protected void start(LifeCycle l) throws Exception
355 // start connectors last
356 if (!(l instanceof Connector))
360 /* ------------------------------------------------------------ */
362 protected void doStop() throws Exception
364 if (isDumpBeforeStop())
367 MultiException mex=new MultiException();
369 // list if graceful futures
370 List<Future<Void>> futures = new ArrayList<>();
372 // First close the network connectors to stop accepting new connections
373 for (Connector connector : _connectors)
374 futures.add(connector.shutdown());
376 // Then tell the contexts that we are shutting down
378 Handler[] gracefuls = getChildHandlersByClass(Graceful.class);
379 for (Handler graceful : gracefuls)
380 futures.add(((Graceful)graceful).shutdown());
382 // Shall we gracefully wait for zero connections?
383 long stopTimeout = getStopTimeout();
386 long stop_by=System.currentTimeMillis()+stopTimeout;
387 LOG.debug("Graceful shutdown {} by ",this,new Date(stop_by));
389 // Wait for shutdowns
390 for (Future<Void> future: futures)
394 if (!future.isDone())
395 future.get(Math.max(1L,stop_by-System.currentTimeMillis()),TimeUnit.MILLISECONDS);
399 mex.add(e.getCause());
404 // Cancel any shutdowns not done
405 for (Future<Void> future: futures)
406 if (!future.isDone())
409 // Now stop the connectors (this will close existing connections)
410 for (Connector connector : _connectors)
422 // And finally stop everything else
432 if (getStopAtShutdown())
433 ShutdownThread.deregister(this);
435 mex.ifExceptionThrow();
439 /* ------------------------------------------------------------ */
440 /* Handle a request from a connection.
441 * Called to handle a request on the connection when either the header has been received,
442 * or after the entire request has been received (for short requests of known length), or
443 * on the dispatch of an async request.
445 public void handle(HttpChannel<?> connection) throws IOException, ServletException
447 final String target=connection.getRequest().getPathInfo();
448 final Request request=connection.getRequest();
449 final Response response=connection.getResponse();
451 if (LOG.isDebugEnabled())
452 LOG.debug(request.getDispatcherType()+" "+target+" on "+connection);
454 if ("*".equals(target))
456 handleOptions(request,response);
457 if (!request.isHandled())
458 handle(target, request, request, response);
461 handle(target, request, request, response);
463 if (LOG.isDebugEnabled())
464 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus()+" handled="+request.isHandled());
467 /* ------------------------------------------------------------ */
468 /* Handle Options request to server
470 protected void handleOptions(Request request,Response response) throws IOException
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();
481 /* ------------------------------------------------------------ */
482 /* Handle a request from a connection.
483 * Called to handle a request on the connection when either the header has been received,
484 * or after the entire request has been received (for short requests of known length), or
485 * on the dispatch of an async request.
487 public void handleAsync(HttpChannel<?> connection) throws IOException, ServletException
489 final HttpChannelState state = connection.getRequest().getHttpChannelState();
490 final AsyncContextEvent event = state.getAsyncContextEvent();
492 final Request baseRequest=connection.getRequest();
493 final String path=event.getPath();
497 // this is a dispatch with a path
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); //we have to assume dispatch path and query are UTF8
507 final String target=baseRequest.getPathInfo();
508 final HttpServletRequest request=(HttpServletRequest)event.getSuppliedRequest();
509 final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse();
511 if (LOG.isDebugEnabled())
513 LOG.debug(request.getDispatcherType()+" "+target+" on "+connection);
514 handle(target, baseRequest, request, response);
515 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus());
518 handle(target, baseRequest, request, response);
522 /* ------------------------------------------------------------ */
523 public void join() throws InterruptedException
525 getThreadPool().join();
528 /* ------------------------------------------------------------ */
530 * @return Returns the sessionIdManager.
532 public SessionIdManager getSessionIdManager()
534 return _sessionIdManager;
537 /* ------------------------------------------------------------ */
539 * @param sessionIdManager The sessionIdManager to set.
541 public void setSessionIdManager(SessionIdManager sessionIdManager)
543 updateBean(_sessionIdManager,sessionIdManager);
544 _sessionIdManager=sessionIdManager;
547 /* ------------------------------------------------------------ */
549 * @see org.eclipse.util.AttributesMap#clearAttributes()
552 public void clearAttributes()
554 Enumeration<String> names = _attributes.getAttributeNames();
555 while (names.hasMoreElements())
556 removeBean(_attributes.getAttribute(names.nextElement()));
557 _attributes.clearAttributes();
560 /* ------------------------------------------------------------ */
562 * @see org.eclipse.util.AttributesMap#getAttribute(java.lang.String)
565 public Object getAttribute(String name)
567 return _attributes.getAttribute(name);
570 /* ------------------------------------------------------------ */
572 * @see org.eclipse.util.AttributesMap#getAttributeNames()
575 public Enumeration<String> getAttributeNames()
577 return AttributesMap.getAttributeNamesCopy(_attributes);
580 /* ------------------------------------------------------------ */
582 * @see org.eclipse.util.AttributesMap#removeAttribute(java.lang.String)
585 public void removeAttribute(String name)
587 Object bean=_attributes.getAttribute(name);
590 _attributes.removeAttribute(name);
593 /* ------------------------------------------------------------ */
595 * @see org.eclipse.util.AttributesMap#setAttribute(java.lang.String, java.lang.Object)
598 public void setAttribute(String name, Object attribute)
601 _attributes.setAttribute(name, attribute);
604 /* ------------------------------------------------------------ */
606 * @return The URI of the first {@link NetworkConnector} and first {@link ContextHandler}, or null
610 NetworkConnector connector=null;
611 for (Connector c: _connectors)
613 if (c instanceof NetworkConnector)
615 connector=(NetworkConnector)c;
623 ContextHandler context = getChildHandlerByClass(ContextHandler.class);
627 String scheme=connector.getDefaultConnectionFactory().getProtocol().startsWith("SSL-")?"https":"http";
629 String host=connector.getHost();
630 if (context!=null && context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
631 host=context.getVirtualHosts()[0];
633 host=InetAddress.getLocalHost().getHostAddress();
635 String path=context==null?null:context.getContextPath();
638 return new URI(scheme,null,host,connector.getLocalPort(),path,null,null);
647 /* ------------------------------------------------------------ */
649 public String toString()
651 return this.getClass().getName()+"@"+Integer.toHexString(hashCode());
655 public void dump(Appendable out,String indent) throws IOException
657 dumpBeans(out,indent,Collections.singleton(new ClassLoaderDump(this.getClass().getClassLoader())));
660 /* ------------------------------------------------------------ */
661 public static void main(String...args) throws Exception
663 System.err.println(getVersion());
666 /* ------------------------------------------------------------ */
667 /* ------------------------------------------------------------ */
668 private static class DateField
671 final HttpField _dateField;
672 public DateField(long seconds, HttpField dateField)
676 _dateField = dateField;