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