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