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