View Javadoc

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