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