View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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  
19  package org.eclipse.jetty.server;
20  
21  import java.io.IOException;
22  import java.net.InetSocketAddress;
23  import java.util.Enumeration;
24  
25  import javax.servlet.AsyncContext;
26  import javax.servlet.ServletException;
27  import javax.servlet.http.HttpServletRequest;
28  import javax.servlet.http.HttpServletResponse;
29  
30  import org.eclipse.jetty.http.HttpGenerator;
31  import org.eclipse.jetty.http.HttpURI;
32  import org.eclipse.jetty.server.handler.HandlerWrapper;
33  import org.eclipse.jetty.server.nio.SelectChannelConnector;
34  import org.eclipse.jetty.util.Attributes;
35  import org.eclipse.jetty.util.AttributesMap;
36  import org.eclipse.jetty.util.LazyList;
37  import org.eclipse.jetty.util.MultiException;
38  import org.eclipse.jetty.util.TypeUtil;
39  import org.eclipse.jetty.util.URIUtil;
40  import org.eclipse.jetty.util.component.Container;
41  import org.eclipse.jetty.util.component.Destroyable;
42  import org.eclipse.jetty.util.component.LifeCycle;
43  import org.eclipse.jetty.util.log.Log;
44  import org.eclipse.jetty.util.log.Logger;
45  import org.eclipse.jetty.util.thread.QueuedThreadPool;
46  import org.eclipse.jetty.util.thread.ShutdownThread;
47  import 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   */
58  public 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             ShutdownThread.register(this);
267             ShutdownMonitor.getInstance().start(); // initialize
268         }
269 
270         LOG.info("jetty-"+__version);
271         HttpGenerator.setServerVersion(__version);
272         
273         MultiException mex=new MultiException();
274 
275         if (_threadPool==null)
276             setThreadPool(new QueuedThreadPool());
277 
278         try
279         {
280             super.doStart();
281         }
282         catch(Throwable e)
283         {
284             mex.add(e);
285         }
286 
287         if (_connectors!=null && mex.size()==0)
288         {
289             for (int i=0;i<_connectors.length;i++)
290             {
291                 try{_connectors[i].start();}
292                 catch(Throwable e)
293                 {
294                     mex.add(e);
295                 }
296             }
297         }
298 
299         if (isDumpAfterStart())
300             dumpStdErr();
301 
302         mex.ifExceptionThrow();
303     }
304 
305     /* ------------------------------------------------------------ */
306     @Override
307     protected void doStop() throws Exception
308     {
309         if (isDumpBeforeStop())
310             dumpStdErr();
311 
312         MultiException mex=new MultiException();
313 
314         if (_graceful>0)
315         {
316             if (_connectors!=null)
317             {
318                 for (int i=_connectors.length;i-->0;)
319                 {
320                     LOG.info("Graceful shutdown {}",_connectors[i]);
321                     try{_connectors[i].close();}catch(Throwable e){mex.add(e);}
322                 }
323             }
324 
325             Handler[] contexts = getChildHandlersByClass(Graceful.class);
326             for (int c=0;c<contexts.length;c++)
327             {
328                 Graceful context=(Graceful)contexts[c];
329                 LOG.info("Graceful shutdown {}",context);
330                 context.setShutdown(true);
331             }
332             Thread.sleep(_graceful);
333         }
334 
335         if (_connectors!=null)
336         {
337             for (int i=_connectors.length;i-->0;)
338                 try{_connectors[i].stop();}catch(Throwable e){mex.add(e);}
339         }
340 
341         try {super.doStop(); } catch(Throwable e) { mex.add(e);}
342 
343         mex.ifExceptionThrow();
344 
345         if (getStopAtShutdown())
346             ShutdownThread.deregister(this);
347     }
348 
349     /* ------------------------------------------------------------ */
350     /* Handle a request from a connection.
351      * Called to handle a request on the connection when either the header has been received,
352      * or after the entire request has been received (for short requests of known length), or
353      * on the dispatch of an async request.
354      */
355     public void handle(AbstractHttpConnection connection) throws IOException, ServletException
356     {
357         final String target=connection.getRequest().getPathInfo();
358         final Request request=connection.getRequest();
359         final Response response=connection.getResponse();
360 
361         if (LOG.isDebugEnabled())
362         {
363             LOG.debug("REQUEST "+target+" on "+connection);
364             handle(target, request, request, response);
365             LOG.debug("RESPONSE "+target+"  "+connection.getResponse().getStatus()+" handled="+request.isHandled());
366         }
367         else
368             handle(target, request, request, response);
369     }
370 
371     /* ------------------------------------------------------------ */
372     /* Handle a request from a connection.
373      * Called to handle a request on the connection when either the header has been received,
374      * or after the entire request has been received (for short requests of known length), or
375      * on the dispatch of an async request.
376      */
377     public void handleAsync(AbstractHttpConnection connection) throws IOException, ServletException
378     {
379         final AsyncContinuation async = connection.getRequest().getAsyncContinuation();
380         final AsyncContinuation.AsyncEventState state = async.getAsyncEventState();
381 
382         final Request baseRequest=connection.getRequest();
383         final String path=state.getPath();
384 
385         if (path!=null)
386         {
387             // this is a dispatch with a path
388             final String contextPath=state.getServletContext().getContextPath();
389             HttpURI uri = new HttpURI(URIUtil.addPaths(contextPath,path));
390             baseRequest.setUri(uri);
391             baseRequest.setRequestURI(null);
392             baseRequest.setPathInfo(baseRequest.getRequestURI());
393             if (uri.getQuery()!=null)
394                 baseRequest.mergeQueryString(uri.getQuery()); //we have to assume dispatch path and query are UTF8
395         }
396 
397         final String target=baseRequest.getPathInfo();
398         final HttpServletRequest request=(HttpServletRequest)async.getRequest();
399         final HttpServletResponse response=(HttpServletResponse)async.getResponse();
400 
401         if (LOG.isDebugEnabled())
402         {
403             LOG.debug("REQUEST "+target+" on "+connection);
404             handle(target, baseRequest, request, response);
405             LOG.debug("RESPONSE "+target+"  "+connection.getResponse().getStatus());
406         }
407         else
408             handle(target, baseRequest, request, response);
409 
410     }
411 
412 
413     /* ------------------------------------------------------------ */
414     public void join() throws InterruptedException
415     {
416         getThreadPool().join();
417     }
418 
419     /* ------------------------------------------------------------ */
420     /* ------------------------------------------------------------ */
421     /**
422      * @return Returns the sessionIdManager.
423      */
424     public SessionIdManager getSessionIdManager()
425     {
426         return _sessionIdManager;
427     }
428 
429     /* ------------------------------------------------------------ */
430     /* ------------------------------------------------------------ */
431     /**
432      * @param sessionIdManager The sessionIdManager to set.
433      */
434     public void setSessionIdManager(SessionIdManager sessionIdManager)
435     {
436         if (_sessionIdManager!=null)
437             removeBean(_sessionIdManager);
438         _container.update(this, _sessionIdManager, sessionIdManager, "sessionIdManager",false);
439         _sessionIdManager = sessionIdManager;
440         if (_sessionIdManager!=null)
441             addBean(_sessionIdManager);
442     }
443 
444     /* ------------------------------------------------------------ */
445     public void setSendServerVersion (boolean sendServerVersion)
446     {
447         _sendServerVersion = sendServerVersion;
448     }
449 
450     /* ------------------------------------------------------------ */
451     public boolean getSendServerVersion()
452     {
453         return _sendServerVersion;
454     }
455 
456     /* ------------------------------------------------------------ */
457     /**
458      * @param sendDateHeader
459      */
460     public void setSendDateHeader(boolean sendDateHeader)
461     {
462         _sendDateHeader = sendDateHeader;
463     }
464 
465     /* ------------------------------------------------------------ */
466     public boolean getSendDateHeader()
467     {
468         return _sendDateHeader;
469     }
470 
471     /* ------------------------------------------------------------ */
472     /** 
473      */
474     @Deprecated
475     public int getMaxCookieVersion()
476     {
477         return 1;
478     }
479 
480     /* ------------------------------------------------------------ */
481     /** 
482      */
483     @Deprecated
484     public void setMaxCookieVersion(int maxCookieVersion)
485     {
486     }
487 
488     /* ------------------------------------------------------------ */
489     /**
490      * Add a LifeCycle object to be started/stopped
491      * along with the Server.
492      * @deprecated Use {@link #addBean(Object)}
493      * @param c
494      */
495     @Deprecated
496     public void addLifeCycle (LifeCycle c)
497     {
498         addBean(c);
499     }
500 
501     /* ------------------------------------------------------------ */
502     /**
503      * Add an associated bean.
504      * The bean will be added to the servers {@link Container}
505      * and if it is a {@link LifeCycle} instance, it will be
506      * started/stopped along with the Server. Any beans that are also
507      * {@link Destroyable}, will be destroyed with the server.
508      * @param o the bean object to add
509      */
510     @Override
511     public boolean addBean(Object o)
512     {
513         if (super.addBean(o))
514         {
515             _container.addBean(o);
516             return true;
517         }
518         return false;
519     }
520 
521     /**
522      * Remove a LifeCycle object to be started/stopped
523      * along with the Server
524      * @deprecated Use {@link #removeBean(Object)}
525      */
526     @Deprecated
527     public void removeLifeCycle (LifeCycle c)
528     {
529         removeBean(c);
530     }
531 
532     /* ------------------------------------------------------------ */
533     /**
534      * Remove an associated bean.
535      */
536     @Override
537     public boolean removeBean (Object o)
538     {
539         if (super.removeBean(o))
540         {
541             _container.removeBean(o);
542             return true;
543         }
544         return false;
545     }
546 
547     /* ------------------------------------------------------------ */
548     /*
549      * @see org.eclipse.util.AttributesMap#clearAttributes()
550      */
551     public void clearAttributes()
552     {
553         _attributes.clearAttributes();
554     }
555 
556     /* ------------------------------------------------------------ */
557     /*
558      * @see org.eclipse.util.AttributesMap#getAttribute(java.lang.String)
559      */
560     public Object getAttribute(String name)
561     {
562         return _attributes.getAttribute(name);
563     }
564 
565     /* ------------------------------------------------------------ */
566     /*
567      * @see org.eclipse.util.AttributesMap#getAttributeNames()
568      */
569     public Enumeration getAttributeNames()
570     {
571         return AttributesMap.getAttributeNamesCopy(_attributes);
572     }
573 
574     /* ------------------------------------------------------------ */
575     /*
576      * @see org.eclipse.util.AttributesMap#removeAttribute(java.lang.String)
577      */
578     public void removeAttribute(String name)
579     {
580         _attributes.removeAttribute(name);
581     }
582 
583     /* ------------------------------------------------------------ */
584     /*
585      * @see org.eclipse.util.AttributesMap#setAttribute(java.lang.String, java.lang.Object)
586      */
587     public void setAttribute(String name, Object attribute)
588     {
589         _attributes.setAttribute(name, attribute);
590     }
591 
592     /* ------------------------------------------------------------ */
593     /**
594      * @return the graceful
595      */
596     public int getGracefulShutdown()
597     {
598         return _graceful;
599     }
600 
601     /* ------------------------------------------------------------ */
602     /**
603      * Set graceful shutdown timeout.  If set, the internal <code>doStop()</code> method will not immediately stop the
604      * server. Instead, all {@link Connector}s will be closed so that new connections will not be accepted
605      * and all handlers that implement {@link Graceful} will be put into the shutdown mode so that no new requests
606      * will be accepted, but existing requests can complete.  The server will then wait the configured timeout
607      * before stopping.
608      * @param timeoutMS the milliseconds to wait for existing request to complete before stopping the server.
609      *
610      */
611     public void setGracefulShutdown(int timeoutMS)
612     {
613         _graceful=timeoutMS;
614     }
615 
616     /* ------------------------------------------------------------ */
617     @Override
618     public String toString()
619     {
620         return this.getClass().getName()+"@"+Integer.toHexString(hashCode());
621     }
622 
623     /* ------------------------------------------------------------ */
624     @Override
625     public void dump(Appendable out,String indent) throws IOException
626     {
627         dumpThis(out);
628         dump(out,indent,TypeUtil.asList(getHandlers()),getBeans(),TypeUtil.asList(_connectors));
629     }
630 
631 
632     /* ------------------------------------------------------------ */
633     public boolean isUncheckedPrintWriter()
634     {
635         return _uncheckedPrintWriter;
636     }
637 
638     /* ------------------------------------------------------------ */
639     public void setUncheckedPrintWriter(boolean unchecked)
640     {
641         _uncheckedPrintWriter=unchecked;
642     }
643 
644 
645     /* ------------------------------------------------------------ */
646     /* A handler that can be gracefully shutdown.
647      * Called by doStop if a {@link #setGracefulShutdown} period is set.
648      * TODO move this somewhere better
649      */
650     public interface Graceful extends Handler
651     {
652         public void setShutdown(boolean shutdown);
653     }
654 
655     /* ------------------------------------------------------------ */
656     public static void main(String...args) throws Exception
657     {
658         System.err.println(getVersion());
659     }
660 }