View Javadoc

1   // ========================================================================
2   // Copyright 2004-2008 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // Licensed under the Apache License, Version 2.0 (the "License");
5   // you may not use this file except in compliance with the License.
6   // You may obtain a copy of the License at 
7   // http://www.apache.org/licenses/LICENSE-2.0
8   // Unless required by applicable law or agreed to in writing, software
9   // distributed under the License is distributed on an "AS IS" BASIS,
10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  // See the License for the specific language governing permissions and
12  // limitations under the License.
13  // ========================================================================
14  
15  package org.mortbay.jetty;
16  
17  import java.io.IOException;
18  import java.lang.reflect.Method;
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Enumeration;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.ListIterator;
25  
26  import javax.servlet.ServletException;
27  
28  import org.mortbay.component.Container;
29  import org.mortbay.component.LifeCycle;
30  import org.mortbay.jetty.bio.SocketConnector;
31  import org.mortbay.jetty.handler.HandlerCollection;
32  import org.mortbay.jetty.handler.HandlerWrapper;
33  import org.mortbay.jetty.security.UserRealm;
34  import org.mortbay.log.Log;
35  import org.mortbay.thread.QueuedThreadPool;
36  import org.mortbay.thread.ThreadPool;
37  import org.mortbay.util.Attributes;
38  import org.mortbay.util.AttributesMap;
39  import org.mortbay.util.LazyList;
40  import org.mortbay.util.MultiException;
41  
42  /* ------------------------------------------------------------ */
43  /** Jetty HTTP Servlet Server.
44   * This class is the main class for the Jetty HTTP Servlet server.
45   * It aggregates Connectors (HTTP request receivers) and request Handlers.
46   * The server is itself a handler and a ThreadPool.  Connectors use the ThreadPool methods
47   * to run jobs that will eventually call the handle method.
48   *
49   *  @org.apache.xbean.XBean  description="Creates an embedded Jetty web server"
50   */
51  public class Server extends HandlerWrapper implements Attributes
52  {
53      public final static String UNKNOWN_VERSION="6.1.x";
54      public final static String SNAPSHOT_VERSION="6.1-SNAPSHOT";
55      private static ShutdownHookThread hookThread = new ShutdownHookThread();
56      private static String _version = (Server.class.getPackage()!=null && Server.class.getPackage().getImplementationVersion()!=null)
57          ?Server.class.getPackage().getImplementationVersion()
58          :UNKNOWN_VERSION;
59  
60      private ThreadPool _threadPool;
61      private Connector[] _connectors;
62      private UserRealm[] _realms;
63      private Container _container=new Container();
64      private SessionIdManager _sessionIdManager;
65      private boolean _sendServerVersion = true; //send Server: header
66      private boolean _sendDateHeader = false; //send Date: header 
67      private AttributesMap _attributes = new AttributesMap();
68      private List _dependentLifeCycles=new ArrayList();
69      private int _graceful=0;
70      
71      /* ------------------------------------------------------------ */
72      public Server()
73      {
74          setServer(this); 
75      }
76      
77      /* ------------------------------------------------------------ */
78      /** Convenience constructor
79       * Creates server and a {@link SocketConnector} at the passed port.
80       */
81      public Server(int port)
82      {
83          setServer(this);
84  
85          Connector connector=new SocketConnector();
86          connector.setPort(port);
87          setConnectors(new Connector[]{connector});
88      }
89  
90  
91      /* ------------------------------------------------------------ */
92      public static String getVersion()
93      {
94          return _version;
95      }
96      
97      /* ------------------------------------------------------------ */
98      /**
99       * @return Returns the container.
100      */
101     public Container getContainer()
102     {
103         return _container;
104     }
105 
106     /* ------------------------------------------------------------ */
107     public boolean getStopAtShutdown()
108     {
109         return hookThread.contains(this);
110     }
111     
112     /* ------------------------------------------------------------ */
113     public void setStopAtShutdown(boolean stop)
114     {
115         if (stop)
116             hookThread.add(this);
117         else
118             hookThread.remove(this);
119     }
120     
121     /* ------------------------------------------------------------ */
122     /**
123      * @return Returns the connectors.
124      */
125     public Connector[] getConnectors()
126     {
127         return _connectors;
128     }
129     
130 
131     /* ------------------------------------------------------------ */
132     public void addConnector(Connector connector)
133     {
134         setConnectors((Connector[])LazyList.addToArray(getConnectors(), connector, Connector.class));
135     }
136 
137     /* ------------------------------------------------------------ */
138     /**
139      * Conveniance method which calls {@link #getConnectors()} and {@link #setConnectors(Connector[])} to 
140      * remove a connector.
141      * @param connector The connector to remove.
142      */
143     public void removeConnector(Connector connector) {
144         setConnectors((Connector[])LazyList.removeFromArray (getConnectors(), connector));
145     }
146 
147     /* ------------------------------------------------------------ */
148     /** Set the connectors for this server.
149      * Each connector has this server set as it's ThreadPool and its Handler.
150      * @param connectors The connectors to set.
151      */
152     public void setConnectors(Connector[] connectors)
153     {
154         if (connectors!=null)
155         {
156             for (int i=0;i<connectors.length;i++)
157                 connectors[i].setServer(this);
158         }
159         
160         _container.update(this, _connectors, connectors, "connector");
161         _connectors = connectors;
162     }
163 
164     /* ------------------------------------------------------------ */
165     /**
166      * @return Returns the threadPool.
167      */
168     public ThreadPool getThreadPool()
169     {
170         return _threadPool;
171     }
172     
173     /* ------------------------------------------------------------ */
174     /**
175      * @param threadPool The threadPool to set.
176      */
177     public void setThreadPool(ThreadPool threadPool)
178     {
179         _container.update(this,_threadPool,threadPool, "threadpool",true);
180         _threadPool = threadPool;
181     }
182 
183     /* ------------------------------------------------------------ */
184     protected void doStart() throws Exception
185     {
186         Log.info("jetty-"+_version);
187         HttpGenerator.setServerVersion(_version);
188         MultiException mex=new MultiException();
189       
190         for (int i=0;_realms !=null && i<_realms.length; i++)
191         {
192             if (_realms[i] instanceof LifeCycle)
193                 ((LifeCycle)_realms[i]).start();
194         }
195 
196         Iterator itor = _dependentLifeCycles.iterator();
197         while (itor.hasNext())
198         {   
199             try
200             {
201                 ((LifeCycle)itor.next()).start(); 
202             }
203             catch (Throwable e) {mex.add(e);}
204         }
205         
206         if (_threadPool==null)
207         {
208             QueuedThreadPool tp=new QueuedThreadPool();
209             setThreadPool(tp);
210         }
211         
212         if (_sessionIdManager!=null)
213             _sessionIdManager.start();
214         
215         try
216         {
217             if (_threadPool instanceof LifeCycle)
218                 ((LifeCycle)_threadPool).start();
219         } 
220         catch(Throwable e) { mex.add(e);}
221         
222         try 
223         { 
224             super.doStart(); 
225         } 
226         catch(Throwable e) 
227         { 
228             Log.warn("Error starting handlers",e);
229         }
230         
231         if (_connectors!=null)
232         {
233             for (int i=0;i<_connectors.length;i++)
234             {
235                 try{_connectors[i].start();}
236                 catch(Throwable e)
237                 {
238                     mex.add(e);
239                 }
240             }
241         }
242         mex.ifExceptionThrow();
243     }
244 
245     /* ------------------------------------------------------------ */
246     protected void doStop() throws Exception
247     {
248         MultiException mex=new MultiException();
249         
250         for (int i=0;_realms !=null && i<_realms.length; i++)
251         {
252             if (_realms[i] instanceof LifeCycle)
253                 ((LifeCycle)_realms[i]).stop();
254         }
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 (!_dependentLifeCycles.isEmpty())
296         {
297             ListIterator itor = _dependentLifeCycles.listIterator(_dependentLifeCycles.size());
298             while (itor.hasPrevious())
299             {
300                 try
301                 {
302                     ((LifeCycle)itor.previous()).stop(); 
303                 }
304                 catch (Throwable e) {mex.add(e);}
305             }
306         }
307        
308         mex.ifExceptionThrow();
309     }
310 
311     /* ------------------------------------------------------------ */
312     /* Handle a request from a connection.
313      * Called to handle a request on the connection when either the header has been received,
314      * or after the entire request has been received (for short requests of known length).
315      */
316     public void handle(HttpConnection connection) throws IOException, ServletException
317     {
318         String target=connection.getRequest().getPathInfo();
319         if (Log.isDebugEnabled())
320         {
321             Log.debug("REQUEST "+target+" on "+connection);
322             handle(target, connection.getRequest(), connection.getResponse(), Handler.REQUEST);
323             Log.debug("RESPONSE "+target+"  "+connection.getResponse().getStatus());
324         }
325         else
326             handle(target, connection.getRequest(), connection.getResponse(), Handler.REQUEST);
327     }
328 
329     /* ------------------------------------------------------------ */
330     public void join() throws InterruptedException 
331     {
332         getThreadPool().join();
333     }
334 
335     /* ------------------------------------------------------------ */
336     /**
337      * @return Map of realm name to UserRealm instances.
338      */
339     public UserRealm[] getUserRealms()
340     {
341         return _realms;
342     }
343     
344     /* ------------------------------------------------------------ */
345     /**
346      * @param realms Map of realm name to UserRealm instances.
347      */
348     public void setUserRealms(UserRealm[] realms)
349     {
350         _container.update(this,_realms,realms, "realm",true);
351         _realms=realms;
352     }
353     
354     /* ------------------------------------------------------------ */
355     public void addUserRealm(UserRealm realm)
356     {
357         setUserRealms((UserRealm[])LazyList.addToArray(getUserRealms(), realm, UserRealm.class));
358     }
359     
360     /* ------------------------------------------------------------ */
361     public void removeUserRealm(UserRealm realm)
362     {
363         setUserRealms((UserRealm[])LazyList.removeFromArray(getUserRealms(), realm));
364     }
365 
366     /* ------------------------------------------------------------ */
367     /* ------------------------------------------------------------ */
368     /**
369      * @return Returns the sessionIdManager.
370      */
371     public SessionIdManager getSessionIdManager()
372     {
373         return _sessionIdManager;
374     }
375 
376     /* ------------------------------------------------------------ */
377     /* ------------------------------------------------------------ */
378     /**
379      * @param sessionIdManager The sessionIdManager to set.
380      */
381     public void setSessionIdManager(SessionIdManager sessionIdManager)
382     {
383         _container.update(this,_sessionIdManager,sessionIdManager, "sessionIdManager",true);
384         _sessionIdManager = sessionIdManager;
385     }
386 
387     /* ------------------------------------------------------------ */
388     public void setSendServerVersion (boolean sendServerVersion)
389     {
390         _sendServerVersion = sendServerVersion;
391     }
392 
393     /* ------------------------------------------------------------ */
394     public boolean getSendServerVersion()
395     {
396         return _sendServerVersion;
397     }
398 
399     /* ------------------------------------------------------------ */
400     /**
401      * @param sendDateHeader
402      */
403     public void setSendDateHeader(boolean sendDateHeader)
404     {
405         _sendDateHeader = sendDateHeader;
406     }
407 
408     /* ------------------------------------------------------------ */
409     public boolean getSendDateHeader()
410     {
411         return _sendDateHeader;
412     }
413     
414     
415     /**
416      * Add a LifeCycle object to be started/stopped
417      * along with the Server.
418      * @param c
419      */
420     public void addLifeCycle (LifeCycle c)
421     {
422         if (c == null)
423             return;
424         if (!_dependentLifeCycles.contains(c)) 
425         {
426             _dependentLifeCycles.add(c);
427             _container.addBean(c);
428         }
429         try
430         {
431             if (isStarted())
432                 ((LifeCycle)c).start();
433         }
434         catch (Exception e)
435         {
436             throw new RuntimeException (e);
437         }
438     }
439     
440     /**
441      * Remove a LifeCycle object to be started/stopped 
442      * along with the Server
443      * @param c
444      */
445     public void removeLifeCycle (LifeCycle c)
446     {
447         if (c == null)
448             return;
449         _dependentLifeCycles.remove(c);
450         _container.removeBean(c);
451     }
452     
453  
454     
455     /* ------------------------------------------------------------ */
456     /* ------------------------------------------------------------ */
457     /* ------------------------------------------------------------ */
458     /**
459      * ShutdownHook thread for stopping all servers.
460      * 
461      * Thread is hooked first time list of servers is changed.
462      */
463     private static class ShutdownHookThread extends Thread
464     {
465         private boolean hooked = false;
466         private ArrayList servers = new ArrayList();
467 
468         /**
469          * Hooks this thread for shutdown.
470          * 
471          * @see java.lang.Runtime#addShutdownHook(java.lang.Thread)
472          */
473         private void createShutdownHook()
474         {
475             if (!Boolean.getBoolean("JETTY_NO_SHUTDOWN_HOOK") && !hooked)
476             {
477                 try
478                 {
479                     Method shutdownHook = java.lang.Runtime.class.getMethod("addShutdownHook", new Class[]
480                     { java.lang.Thread.class});
481                     shutdownHook.invoke(Runtime.getRuntime(), new Object[]
482                     { this});
483                     this.hooked = true;
484                 }
485                 catch (Exception e)
486                 {
487                     if (Log.isDebugEnabled())
488                         Log.debug("No shutdown hook in JVM ", e);
489                 }
490             }
491         }
492 
493         /**
494          * Add Server to servers list.
495          */
496         public boolean add(Server server)
497         {
498             createShutdownHook();
499             return this.servers.add(server);
500         }
501 
502         /**
503          * Contains Server in servers list?
504          */
505         public boolean contains(Server server)
506         {
507             return this.servers.contains(server);
508         }
509 
510         /**
511          * Append all Servers from Collection
512          */
513         public boolean addAll(Collection c)
514         {
515             createShutdownHook();
516             return this.servers.addAll(c);
517         }
518 
519         /**
520          * Clear list of Servers.
521          */
522         public void clear()
523         {
524             createShutdownHook();
525             this.servers.clear();
526         }
527 
528         /**
529          * Remove Server from list.
530          */
531         public boolean remove(Server server)
532         {
533             createShutdownHook();
534             return this.servers.remove(server);
535         }
536 
537         /**
538          * Remove all Servers in Collection from list.
539          */
540         public boolean removeAll(Collection c)
541         {
542             createShutdownHook();
543             return this.servers.removeAll(c);
544         }
545 
546         /**
547          * Stop all Servers in list.
548          */
549         public void run()
550         {
551             setName("Shutdown");
552             Log.info("Shutdown hook executing");
553             Iterator it = servers.iterator();
554             while (it.hasNext())
555             {
556                 Server svr = (Server) it.next();
557                 if (svr == null)
558                     continue;
559                 try
560                 {
561                     svr.stop();
562                 }
563                 catch (Exception e)
564                 {
565                     Log.warn(e);
566                 }
567                 Log.info("Shutdown hook complete");
568 
569                 // Try to avoid JVM crash
570                 try
571                 {
572                     Thread.sleep(1000);
573                 }
574                 catch (Exception e)
575                 {
576                     Log.warn(e);
577                 }
578             }
579         }
580     }
581 
582     
583     
584 
585     /* ------------------------------------------------------------ */
586     /**
587      */
588     public void addHandler(Handler handler)
589     {
590         if (getHandler() == null) 
591             setHandler(handler);
592         else if (getHandler() instanceof HandlerCollection)
593             ((HandlerCollection)getHandler()).addHandler(handler);
594         else
595         {
596             HandlerCollection collection=new HandlerCollection();
597             collection.setHandlers(new Handler[]{getHandler(),handler});
598             setHandler(collection);
599         }
600     }
601     
602     /* ------------------------------------------------------------ */
603     /**
604      */
605     public void removeHandler(Handler handler)
606     {
607         if (getHandler() instanceof HandlerCollection)
608             ((HandlerCollection)getHandler()).removeHandler(handler);
609     }
610 
611     /* ------------------------------------------------------------ */
612     /**
613      */
614     public Handler[] getHandlers()
615     {
616         if (getHandler() instanceof HandlerCollection)
617             return ((HandlerCollection)getHandler()).getHandlers();
618         
619         return null;
620     }
621     
622     /* ------------------------------------------------------------ */
623     /**
624      */
625     public void setHandlers(Handler[] handlers)
626     {
627         HandlerCollection collection;
628         if (getHandler() instanceof HandlerCollection)
629             collection=(HandlerCollection)getHandler();
630         else
631         {
632             collection=new HandlerCollection();
633             setHandler(collection);
634         }
635             
636         collection.setHandlers(handlers);
637     }
638 
639     /* ------------------------------------------------------------ */
640     /* 
641      * @see org.mortbay.util.AttributesMap#clearAttributes()
642      */
643     public void clearAttributes()
644     {
645         _attributes.clearAttributes();
646     }
647 
648     /* ------------------------------------------------------------ */
649     /* 
650      * @see org.mortbay.util.AttributesMap#getAttribute(java.lang.String)
651      */
652     public Object getAttribute(String name)
653     {
654         return _attributes.getAttribute(name);
655     }
656 
657     /* ------------------------------------------------------------ */
658     /* 
659      * @see org.mortbay.util.AttributesMap#getAttributeNames()
660      */
661     public Enumeration getAttributeNames()
662     {
663         return AttributesMap.getAttributeNamesCopy(_attributes);
664     }
665 
666     /* ------------------------------------------------------------ */
667     /* 
668      * @see org.mortbay.util.AttributesMap#removeAttribute(java.lang.String)
669      */
670     public void removeAttribute(String name)
671     {
672         _attributes.removeAttribute(name);
673     }
674 
675     /* ------------------------------------------------------------ */
676     /* 
677      * @see org.mortbay.util.AttributesMap#setAttribute(java.lang.String, java.lang.Object)
678      */
679     public void setAttribute(String name, Object attribute)
680     {
681         _attributes.setAttribute(name, attribute);
682     }
683 
684     /* ------------------------------------------------------------ */
685     /**
686      * @return the graceful
687      */
688     public int getGracefulShutdown()
689     {
690         return _graceful;
691     }
692 
693     /* ------------------------------------------------------------ */
694     /**
695      * Set graceful shutdown timeout.  If set, the {@link #doStop()} method will not immediately stop the 
696      * server. Instead, all {@link Connector}s will be closed so that new connections will not be accepted
697      * and all handlers that implement {@link Graceful} will be put into the shutdown mode so that no new requests
698      * will be accepted, but existing requests can complete.  The server will then wait the configured timeout 
699      * before stopping.
700      * @param timeoutMS the milliseconds to wait for existing request to complete before stopping the server.
701      * 
702      */
703     public void setGracefulShutdown(int timeoutMS)
704     {
705         _graceful=timeoutMS;
706     }
707 
708     /* ------------------------------------------------------------ */
709     /* A component that can be gracefully shutdown.
710      * Called by doStop if a {@link #setGracefulShutdown} period is set.
711      */
712     public interface Graceful
713     {
714         public void setShutdown(boolean shutdown);
715     }
716 }