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 }