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