1// 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. 8// 9// The Eclipse Public License is available at 10// http://www.eclipse.org/legal/epl-v10.html 11// 12// The Apache License v2.0 is available at 13// http://www.opensource.org/licenses/apache2.0.php 14// 15// You may elect to redistribute this code under either of these licenses. 16// ======================================================================== 17// 18 19package org.eclipse.jetty.server; 20 21import java.io.IOException; 22import java.net.InetSocketAddress; 23import java.util.Enumeration; 24 25import javax.servlet.AsyncContext; 26import javax.servlet.ServletException; 27import javax.servlet.http.HttpServletRequest; 28import javax.servlet.http.HttpServletResponse; 29 30import org.eclipse.jetty.http.HttpGenerator; 31import org.eclipse.jetty.http.HttpURI; 32import org.eclipse.jetty.server.handler.HandlerWrapper; 33import org.eclipse.jetty.server.nio.SelectChannelConnector; 34import org.eclipse.jetty.util.Attributes; 35import org.eclipse.jetty.util.AttributesMap; 36import org.eclipse.jetty.util.LazyList; 37import org.eclipse.jetty.util.MultiException; 38import org.eclipse.jetty.util.TypeUtil; 39import org.eclipse.jetty.util.URIUtil; 40import org.eclipse.jetty.util.component.Container; 41import org.eclipse.jetty.util.component.Destroyable; 42import org.eclipse.jetty.util.component.LifeCycle; 43import org.eclipse.jetty.util.log.Log; 44import org.eclipse.jetty.util.log.Logger; 45import org.eclipse.jetty.util.thread.QueuedThreadPool; 46import org.eclipse.jetty.util.thread.ShutdownThread; 47import org.eclipse.jetty.util.thread.ThreadPool; 48 49/* ------------------------------------------------------------ */ 50/** Jetty HTTP Servlet Server. 51 * This class is the main class for the Jetty HTTP Servlet server. 52 * It aggregates Connectors (HTTP request receivers) and request Handlers. 53 * The server is itself a handler and a ThreadPool. Connectors use the ThreadPool methods 54 * to run jobs that will eventually call the handle method. 55 * 56 * @org.apache.xbean.XBean description="Creates an embedded Jetty web server" 57 */ 58public class Server extends HandlerWrapper implements Attributes 59{ 60 private static final Logger LOG = Log.getLogger(Server.class); 61 62 private static final String __version; 63 static 64 { 65 if (Server.class.getPackage()!=null && 66 "Eclipse.org - Jetty".equals(Server.class.getPackage().getImplementationVendor()) && 67 Server.class.getPackage().getImplementationVersion()!=null) 68 __version=Server.class.getPackage().getImplementationVersion(); 69 else 70 __version=System.getProperty("jetty.version","8.y.z-SNAPSHOT"); 71 } 72 73 private final Container _container=new Container(); 74 private final AttributesMap _attributes = new AttributesMap(); 75 private ThreadPool _threadPool; 76 private Connector[] _connectors; 77 private SessionIdManager _sessionIdManager; 78 private boolean _sendServerVersion = true; //send Server: header 79 private boolean _sendDateHeader = false; //send Date: header 80 private int _graceful=0; 81 private boolean _stopAtShutdown; 82 private boolean _dumpAfterStart=false; 83 private boolean _dumpBeforeStop=false; 84 private boolean _uncheckedPrintWriter=false; 85 86 87 /* ------------------------------------------------------------ */ 88 public Server() 89 { 90 setServer(this); 91 } 92 93 /* ------------------------------------------------------------ */ 94 /** Convenience constructor 95 * Creates server and a {@link SelectChannelConnector} at the passed port. 96 */ 97 public Server(int port) 98 { 99 setServer(this); 100 101 Connector connector=new SelectChannelConnector(); 102 connector.setPort(port); 103 setConnectors(new Connector[]{connector}); 104 } 105 106 /* ------------------------------------------------------------ */ 107 /** Convenience constructor 108 * Creates server and a {@link SelectChannelConnector} at the passed address. 109 */ 110 public Server(InetSocketAddress addr) 111 { 112 setServer(this); 113 114 Connector connector=new SelectChannelConnector(); 115 connector.setHost(addr.getHostName()); 116 connector.setPort(addr.getPort()); 117 setConnectors(new Connector[]{connector}); 118 } 119 120 121 /* ------------------------------------------------------------ */ 122 public static String getVersion() 123 { 124 return __version; 125 } 126 127 /* ------------------------------------------------------------ */ 128 /** 129 * @return Returns the container. 130 */ 131 public Container getContainer() 132 { 133 return _container; 134 } 135 136 /* ------------------------------------------------------------ */ 137 public boolean getStopAtShutdown() 138 { 139 return _stopAtShutdown; 140 } 141 142 /* ------------------------------------------------------------ */ 143 public void setStopAtShutdown(boolean stop) 144 { 145 //if we now want to stop 146 if (stop) 147 { 148 //and we weren't stopping before 149 if (!_stopAtShutdown) 150 { 151 //only register to stop if we're already started (otherwise we'll do it in doStart()) 152 if (isStarted()) 153 ShutdownThread.register(this); 154 } 155 } 156 else 157 ShutdownThread.deregister(this); 158 159 _stopAtShutdown=stop; 160 } 161 162 /* ------------------------------------------------------------ */ 163 /** 164 * @return Returns the connectors. 165 */ 166 public Connector[] getConnectors() 167 { 168 return _connectors; 169 } 170 171 /* ------------------------------------------------------------ */ 172 public void addConnector(Connector connector) 173 { 174 setConnectors((Connector[])LazyList.addToArray(getConnectors(), connector, Connector.class)); 175 } 176 177 /* ------------------------------------------------------------ */ 178 /** 179 * Conveniance method which calls {@link #getConnectors()} and {@link #setConnectors(Connector[])} to 180 * remove a connector. 181 * @param connector The connector to remove. 182 */ 183 public void removeConnector(Connector connector) { 184 setConnectors((Connector[])LazyList.removeFromArray (getConnectors(), connector)); 185 } 186 187 /* ------------------------------------------------------------ */ 188 /** Set the connectors for this server. 189 * Each connector has this server set as it's ThreadPool and its Handler. 190 * @param connectors The connectors to set. 191 */ 192 public void setConnectors(Connector[] connectors) 193 { 194 if (connectors!=null) 195 { 196 for (int i=0;i<connectors.length;i++) 197 connectors[i].setServer(this); 198 } 199 200 _container.update(this, _connectors, connectors, "connector"); 201 _connectors = connectors; 202 } 203 204 /* ------------------------------------------------------------ */ 205 /** 206 * @return Returns the threadPool. 207 */ 208 public ThreadPool getThreadPool() 209 { 210 return _threadPool; 211 } 212 213 /* ------------------------------------------------------------ */ 214 /** 215 * @param threadPool The threadPool to set. 216 */ 217 public void setThreadPool(ThreadPool threadPool) 218 { 219 if (_threadPool!=null) 220 removeBean(_threadPool); 221 _container.update(this, _threadPool, threadPool, "threadpool",false); 222 _threadPool = threadPool; 223 if (_threadPool!=null) 224 addBean(_threadPool); 225 } 226 227 /** 228 * @return true if {@link #dumpStdErr()} is called after starting 229 */ 230 public boolean isDumpAfterStart() 231 { 232 return _dumpAfterStart; 233 } 234 235 /** 236 * @param dumpAfterStart true if {@link #dumpStdErr()} is called after starting 237 */ 238 public void setDumpAfterStart(boolean dumpAfterStart) 239 { 240 _dumpAfterStart = dumpAfterStart; 241 } 242 243 /** 244 * @return true if {@link #dumpStdErr()} is called before stopping 245 */ 246 public boolean isDumpBeforeStop() 247 { 248 return _dumpBeforeStop; 249 } 250 251 /** 252 * @param dumpBeforeStop true if {@link #dumpStdErr()} is called before stopping 253 */ 254 public void setDumpBeforeStop(boolean dumpBeforeStop) 255 { 256 _dumpBeforeStop = dumpBeforeStop; 257 } 258 259 260 261 /* ------------------------------------------------------------ */ 262 @Override 263 protected void doStart() throws Exception 264 { 265 if (getStopAtShutdown()) 266 { 267 ShutdownThread.register(this); 268 } 269 270 ShutdownMonitor.getInstance().start(); // initialize 271 272 LOG.info("jetty-"+__version); 273 HttpGenerator.setServerVersion(__version); 274 275 MultiException mex=new MultiException(); 276 277 if (_threadPool==null) 278 setThreadPool(new QueuedThreadPool()); 279 280 try 281 { 282 super.doStart(); 283 } 284 catch(Throwable e) 285 { 286 mex.add(e); 287 } 288 289 if (_connectors!=null && mex.size()==0) 290 { 291 for (int i=0;i<_connectors.length;i++) 292 { 293 try{_connectors[i].start();} 294 catch(Throwable e) 295 { 296 mex.add(e); 297 } 298 } 299 } 300 301 if (isDumpAfterStart()) 302 dumpStdErr(); 303 304 mex.ifExceptionThrow(); 305 } 306 307 /* ------------------------------------------------------------ */ 308 @Override 309 protected void doStop() throws Exception 310 { 311 if (isDumpBeforeStop()) 312 dumpStdErr(); 313 314 MultiException mex=new MultiException(); 315 316 if (_graceful>0) 317 { 318 if (_connectors!=null) 319 { 320 for (int i=_connectors.length;i-->0;) 321 { 322 LOG.info("Graceful shutdown {}",_connectors[i]); 323 try{_connectors[i].close();}catch(Throwable e){mex.add(e);} 324 } 325 } 326 327 Handler[] contexts = getChildHandlersByClass(Graceful.class); 328 for (int c=0;c<contexts.length;c++) 329 { 330 Graceful context=(Graceful)contexts[c]; 331 LOG.info("Graceful shutdown {}",context); 332 context.setShutdown(true); 333 } 334 Thread.sleep(_graceful); 335 } 336 337 if (_connectors!=null) 338 { 339 for (int i=_connectors.length;i-->0;) 340 try{_connectors[i].stop();}catch(Throwable e){mex.add(e);} 341 } 342 343 try {super.doStop(); } catch(Throwable e) { mex.add(e);} 344 345 mex.ifExceptionThrow(); 346 347 if (getStopAtShutdown()) 348 ShutdownThread.deregister(this); 349 } 350 351 /* ------------------------------------------------------------ */ 352 /* Handle a request from a connection. 353 * Called to handle a request on the connection when either the header has been received, 354 * or after the entire request has been received (for short requests of known length), or 355 * on the dispatch of an async request. 356 */ 357 public void handle(AbstractHttpConnection connection) throws IOException, ServletException 358 { 359 final String target=connection.getRequest().getPathInfo(); 360 final Request request=connection.getRequest(); 361 final Response response=connection.getResponse(); 362 363 if (LOG.isDebugEnabled()) 364 { 365 LOG.debug("REQUEST "+target+" on "+connection); 366 handle(target, request, request, response); 367 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus()+" handled="+request.isHandled()); 368 } 369 else 370 handle(target, request, request, response); 371 } 372 373 /* ------------------------------------------------------------ */ 374 /* Handle a request from a connection. 375 * Called to handle a request on the connection when either the header has been received, 376 * or after the entire request has been received (for short requests of known length), or 377 * on the dispatch of an async request. 378 */ 379 public void handleAsync(AbstractHttpConnection connection) throws IOException, ServletException 380 { 381 final AsyncContinuation async = connection.getRequest().getAsyncContinuation(); 382 final AsyncContinuation.AsyncEventState state = async.getAsyncEventState(); 383 384 final Request baseRequest=connection.getRequest(); 385 final String path=state.getPath(); 386 387 if (path!=null) 388 { 389 // this is a dispatch with a path 390 final String contextPath=state.getServletContext().getContextPath(); 391 HttpURI uri = new HttpURI(URIUtil.addPaths(contextPath,path)); 392 baseRequest.setUri(uri); 393 baseRequest.setRequestURI(null); 394 baseRequest.setPathInfo(baseRequest.getRequestURI()); 395 if (uri.getQuery()!=null) 396 baseRequest.mergeQueryString(uri.getQuery()); //we have to assume dispatch path and query are UTF8 397 } 398 399 final String target=baseRequest.getPathInfo(); 400 final HttpServletRequest request=(HttpServletRequest)async.getRequest(); 401 final HttpServletResponse response=(HttpServletResponse)async.getResponse(); 402 403 if (LOG.isDebugEnabled()) 404 { 405 LOG.debug("REQUEST "+target+" on "+connection); 406 handle(target, baseRequest, request, response); 407 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus()); 408 } 409 else 410 handle(target, baseRequest, request, response); 411 412 } 413 414 415 /* ------------------------------------------------------------ */ 416 public void join() throws InterruptedException 417 { 418 getThreadPool().join(); 419 } 420 421 /* ------------------------------------------------------------ */ 422 /* ------------------------------------------------------------ */ 423 /** 424 * @return Returns the sessionIdManager. 425 */ 426 public SessionIdManager getSessionIdManager() 427 { 428 return _sessionIdManager; 429 } 430 431 /* ------------------------------------------------------------ */ 432 /* ------------------------------------------------------------ */ 433 /** 434 * @param sessionIdManager The sessionIdManager to set. 435 */ 436 public void setSessionIdManager(SessionIdManager sessionIdManager) 437 { 438 if (_sessionIdManager!=null) 439 removeBean(_sessionIdManager); 440 _container.update(this, _sessionIdManager, sessionIdManager, "sessionIdManager",false); 441 _sessionIdManager = sessionIdManager; 442 if (_sessionIdManager!=null) 443 addBean(_sessionIdManager); 444 } 445 446 /* ------------------------------------------------------------ */ 447 public void setSendServerVersion (boolean sendServerVersion) 448 { 449 _sendServerVersion = sendServerVersion; 450 } 451 452 /* ------------------------------------------------------------ */ 453 public boolean getSendServerVersion() 454 { 455 return _sendServerVersion; 456 } 457 458 /* ------------------------------------------------------------ */ 459 /** 460 * @param sendDateHeader 461 */ 462 public void setSendDateHeader(boolean sendDateHeader) 463 { 464 _sendDateHeader = sendDateHeader; 465 } 466 467 /* ------------------------------------------------------------ */ 468 public boolean getSendDateHeader() 469 { 470 return _sendDateHeader; 471 } 472 473 /* ------------------------------------------------------------ */ 474 /** 475 */ 476 @Deprecated 477 public int getMaxCookieVersion() 478 { 479 return 1; 480 } 481 482 /* ------------------------------------------------------------ */ 483 /** 484 */ 485 @Deprecated 486 public void setMaxCookieVersion(int maxCookieVersion) 487 { 488 } 489 490 /* ------------------------------------------------------------ */ 491 /** 492 * Add a LifeCycle object to be started/stopped 493 * along with the Server. 494 * @deprecated Use {@link #addBean(Object)} 495 * @param c 496 */ 497 @Deprecated 498 public void addLifeCycle (LifeCycle c) 499 { 500 addBean(c); 501 } 502 503 /* ------------------------------------------------------------ */ 504 /** 505 * Add an associated bean. 506 * The bean will be added to the servers {@link Container} 507 * and if it is a {@link LifeCycle} instance, it will be 508 * started/stopped along with the Server. Any beans that are also 509 * {@link Destroyable}, will be destroyed with the server. 510 * @param o the bean object to add 511 */ 512 @Override 513 public boolean addBean(Object o) 514 { 515 if (super.addBean(o)) 516 { 517 _container.addBean(o); 518 return true; 519 } 520 return false; 521 } 522 523 /** 524 * Remove a LifeCycle object to be started/stopped 525 * along with the Server 526 * @deprecated Use {@link #removeBean(Object)} 527 */ 528 @Deprecated 529 public void removeLifeCycle (LifeCycle c) 530 { 531 removeBean(c); 532 } 533 534 /* ------------------------------------------------------------ */ 535 /** 536 * Remove an associated bean. 537 */ 538 @Override 539 public boolean removeBean (Object o) 540 { 541 if (super.removeBean(o)) 542 { 543 _container.removeBean(o); 544 return true; 545 } 546 return false; 547 } 548 549 /* ------------------------------------------------------------ */ 550 /* 551 * @see org.eclipse.util.AttributesMap#clearAttributes() 552 */ 553 public void clearAttributes() 554 { 555 _attributes.clearAttributes(); 556 } 557 558 /* ------------------------------------------------------------ */ 559 /* 560 * @see org.eclipse.util.AttributesMap#getAttribute(java.lang.String) 561 */ 562 public Object getAttribute(String name) 563 { 564 return _attributes.getAttribute(name); 565 } 566 567 /* ------------------------------------------------------------ */ 568 /* 569 * @see org.eclipse.util.AttributesMap#getAttributeNames() 570 */ 571 public Enumeration getAttributeNames() 572 { 573 return AttributesMap.getAttributeNamesCopy(_attributes); 574 } 575 576 /* ------------------------------------------------------------ */ 577 /* 578 * @see org.eclipse.util.AttributesMap#removeAttribute(java.lang.String) 579 */ 580 public void removeAttribute(String name) 581 { 582 _attributes.removeAttribute(name); 583 } 584 585 /* ------------------------------------------------------------ */ 586 /* 587 * @see org.eclipse.util.AttributesMap#setAttribute(java.lang.String, java.lang.Object) 588 */ 589 public void setAttribute(String name, Object attribute) 590 { 591 _attributes.setAttribute(name, attribute); 592 } 593 594 /* ------------------------------------------------------------ */ 595 /** 596 * @return the graceful 597 */ 598 public int getGracefulShutdown() 599 { 600 return _graceful; 601 } 602 603 /* ------------------------------------------------------------ */ 604 /** 605 * Set graceful shutdown timeout. If set, the internal <code>doStop()</code> method will not immediately stop the 606 * server. Instead, all {@link Connector}s will be closed so that new connections will not be accepted 607 * and all handlers that implement {@link Graceful} will be put into the shutdown mode so that no new requests 608 * will be accepted, but existing requests can complete. The server will then wait the configured timeout 609 * before stopping. 610 * @param timeoutMS the milliseconds to wait for existing request to complete before stopping the server. 611 * 612 */ 613 public void setGracefulShutdown(int timeoutMS) 614 { 615 _graceful=timeoutMS; 616 } 617 618 /* ------------------------------------------------------------ */ 619 @Override 620 public String toString() 621 { 622 return this.getClass().getName()+"@"+Integer.toHexString(hashCode()); 623 } 624 625 /* ------------------------------------------------------------ */ 626 @Override 627 public void dump(Appendable out,String indent) throws IOException 628 { 629 dumpThis(out); 630 dump(out,indent,TypeUtil.asList(getHandlers()),getBeans(),TypeUtil.asList(_connectors)); 631 } 632 633 634 /* ------------------------------------------------------------ */ 635 public boolean isUncheckedPrintWriter() 636 { 637 return _uncheckedPrintWriter; 638 } 639 640 /* ------------------------------------------------------------ */ 641 public void setUncheckedPrintWriter(boolean unchecked) 642 { 643 _uncheckedPrintWriter=unchecked; 644 } 645 646 647 /* ------------------------------------------------------------ */ 648 /* A handler that can be gracefully shutdown. 649 * Called by doStop if a {@link #setGracefulShutdown} period is set. 650 * TODO move this somewhere better 651 */ 652 public interface Graceful extends Handler 653 { 654 public void setShutdown(boolean shutdown); 655 } 656 657 /* ------------------------------------------------------------ */ 658 public static void main(String...args) throws Exception 659 { 660 System.err.println(getVersion()); 661 } 662} 663