View Javadoc

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