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