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 }