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