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