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