View Javadoc

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