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