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