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