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