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.InetAddress;
18  import java.net.Socket;
19  import java.net.UnknownHostException;
20  import java.util.concurrent.atomic.AtomicLong;
21  
22  import javax.servlet.ServletRequest;
23  
24  import org.eclipse.jetty.http.HttpBuffers;
25  import org.eclipse.jetty.http.HttpFields;
26  import org.eclipse.jetty.http.HttpHeaders;
27  import org.eclipse.jetty.http.HttpSchemes;
28  import org.eclipse.jetty.io.Buffer;
29  import org.eclipse.jetty.io.ByteArrayBuffer;
30  import org.eclipse.jetty.io.Connection;
31  import org.eclipse.jetty.io.EndPoint;
32  import org.eclipse.jetty.io.EofException;
33  import org.eclipse.jetty.util.component.AggregateLifeCycle;
34  import org.eclipse.jetty.util.component.Dumpable;
35  import org.eclipse.jetty.util.component.LifeCycle;
36  import org.eclipse.jetty.util.log.Log;
37  import org.eclipse.jetty.util.statistic.SampleStatistic;
38  import org.eclipse.jetty.util.statistic.CounterStatistic;
39  import org.eclipse.jetty.util.thread.ThreadPool;
40  
41  /**
42   * Abstract Connector implementation. This abstract implementation of the
43   * Connector interface provides:
44   * <ul>
45   * <li>AbstractLifeCycle implementation</li>
46   * <li>Implementations for connector getters and setters</li>
47   * <li>Buffer management</li>
48   * <li>Socket configuration</li>
49   * <li>Base acceptor thread</li>
50   * <li>Optional reverse proxy headers checking</li>
51   * </ul>
52   * 
53   * 
54   */
55  public abstract class AbstractConnector extends HttpBuffers implements Connector, Dumpable
56  {
57      private String _name;
58  
59      private Server _server;
60      private ThreadPool _threadPool;
61      private String _host;
62      private int _port = 0;
63      private String _integralScheme = HttpSchemes.HTTPS;
64      private int _integralPort = 0;
65      private String _confidentialScheme = HttpSchemes.HTTPS;
66      private int _confidentialPort = 0;
67      private int _acceptQueueSize = 0;
68      private int _acceptors = 1;
69      private int _acceptorPriorityOffset = 0;
70      private boolean _useDNS;
71      private boolean _forwarded;
72      private String _hostHeader;
73      
74      private String _forwardedHostHeader = "X-Forwarded-Host"; 
75      private String _forwardedServerHeader = "X-Forwarded-Server"; 
76      private String _forwardedForHeader = "X-Forwarded-For"; 
77      private String _forwardedProtoHeader = "X-Forwarded-Proto"; 
78      private boolean _reuseAddress = true;
79  
80      protected int _maxIdleTime = 200000;
81      protected int _lowResourceMaxIdleTime = -1;
82      protected int _soLingerTime = -1;
83  
84      private transient Thread[] _acceptorThread;
85  
86      private final AtomicLong _statsStartedAt = new AtomicLong(-1L);
87  
88      /** connections to server */
89      private final CounterStatistic _connectionStats = new CounterStatistic(); 
90      /** requests per connection */
91      private final SampleStatistic _requestStats = new SampleStatistic(); 
92      /** duration of a connection */
93      private final SampleStatistic _connectionDurationStats = new SampleStatistic(); 
94  
95      /* ------------------------------------------------------------ */
96      /**
97       */
98      public AbstractConnector()
99      {
100     }
101 
102     /* ------------------------------------------------------------ */
103     @Override
104     public Buffer newRequestBuffer(int size)
105     {
106         return new ByteArrayBuffer(size);
107     }
108 
109     /* ------------------------------------------------------------ */
110     @Override
111     public Buffer newRequestHeader(int size)
112     {
113         return new ByteArrayBuffer(size);
114     }
115 
116     /* ------------------------------------------------------------ */
117     @Override
118     public Buffer newResponseBuffer(int size)
119     {
120         return new ByteArrayBuffer(size);
121     }
122 
123     /* ------------------------------------------------------------ */
124     @Override
125     public Buffer newResponseHeader(int size)
126     {
127         return new ByteArrayBuffer(size);
128     }
129 
130     /* ------------------------------------------------------------ */
131     @Override
132     protected boolean isRequestHeader(Buffer buffer)
133     {
134         return true;
135     }
136 
137     /* ------------------------------------------------------------ */
138     @Override
139     protected boolean isResponseHeader(Buffer buffer)
140     {
141         return true;
142     }
143 
144     /* ------------------------------------------------------------ */
145     /*
146      */
147     public Server getServer()
148     {
149         return _server;
150     }
151 
152     /* ------------------------------------------------------------ */
153     public void setServer(Server server)
154     {
155         _server = server;
156     }
157 
158     /* ------------------------------------------------------------ */
159     /*
160      * @see org.eclipse.jetty.http.HttpListener#getHttpServer()
161      */
162     public ThreadPool getThreadPool()
163     {
164         return _threadPool;
165     }
166 
167     /* ------------------------------------------------------------ */
168     public void setThreadPool(ThreadPool pool)
169     {
170         _threadPool = pool;
171     }
172 
173     /* ------------------------------------------------------------ */
174     /**
175      */
176     public void setHost(String host)
177     {
178         _host = host;
179     }
180 
181     /* ------------------------------------------------------------ */
182     /*
183      */
184     public String getHost()
185     {
186         return _host;
187     }
188 
189     /* ------------------------------------------------------------ */
190     /*
191      * @see org.eclipse.jetty.server.server.HttpListener#setPort(int)
192      */
193     public void setPort(int port)
194     {
195         _port = port;
196     }
197 
198     /* ------------------------------------------------------------ */
199     /*
200      * @see org.eclipse.jetty.server.server.HttpListener#getPort()
201      */
202     public int getPort()
203     {
204         return _port;
205     }
206 
207     /* ------------------------------------------------------------ */
208     /**
209      * @return Returns the maxIdleTime.
210      */
211     public int getMaxIdleTime()
212     {
213         return _maxIdleTime;
214     }
215 
216     /* ------------------------------------------------------------ */
217     /**
218      * Set the maximum Idle time for a connection, which roughly translates to
219      * the {@link Socket#setSoTimeout(int)} call, although with NIO
220      * implementations other mechanisms may be used to implement the timeout.
221      * The max idle time is applied:
222      * <ul>
223      * <li>When waiting for a new request to be received on a connection</li>
224      * <li>When reading the headers and content of a request</li>
225      * <li>When writing the headers and content of a response</li>
226      * </ul>
227      * Jetty interprets this value as the maximum time between some progress
228      * being made on the connection. So if a single byte is read or written,
229      * then the timeout (if implemented by jetty) is reset. However, in many
230      * instances, the reading/writing is delegated to the JVM, and the semantic
231      * is more strictly enforced as the maximum time a single read/write
232      * operation can take. Note, that as Jetty supports writes of memory mapped
233      * file buffers, then a write may take many 10s of seconds for large content
234      * written to a slow device.
235      * <p>
236      * Previously, Jetty supported separate idle timeouts and IO operation
237      * timeouts, however the expense of changing the value of soTimeout was
238      * significant, so these timeouts were merged. With the advent of NIO, it
239      * may be possible to again differentiate these values (if there is demand).
240      * 
241      * @param maxIdleTime
242      *            The maxIdleTime to set.
243      */
244     public void setMaxIdleTime(int maxIdleTime)
245     {
246         _maxIdleTime = maxIdleTime;
247     }
248 
249     /* ------------------------------------------------------------ */
250     /**
251      * @return Returns the maxIdleTime when resources are low.
252      */
253     public int getLowResourcesMaxIdleTime()
254     {
255         return _lowResourceMaxIdleTime;
256     }
257 
258     /* ------------------------------------------------------------ */
259     /**
260      * @param maxIdleTime
261      *            The maxIdleTime to set when resources are low.
262      */
263     public void setLowResourcesMaxIdleTime(int maxIdleTime)
264     {
265         _lowResourceMaxIdleTime = maxIdleTime;
266     }
267 
268     /* ------------------------------------------------------------ */
269     /**
270      * @return Returns the maxIdleTime when resources are low.
271      * @deprecated
272      */
273     public final int getLowResourceMaxIdleTime()
274     {
275         return getLowResourcesMaxIdleTime();
276     }
277 
278     /* ------------------------------------------------------------ */
279     /**
280      * @param maxIdleTime
281      *            The maxIdleTime to set when resources are low.
282      * @deprecated
283      */
284     public final void setLowResourceMaxIdleTime(int maxIdleTime)
285     {
286         setLowResourcesMaxIdleTime(maxIdleTime);
287     }
288 
289     /* ------------------------------------------------------------ */
290     /**
291      * @return Returns the soLingerTime.
292      */
293     public int getSoLingerTime()
294     {
295         return _soLingerTime;
296     }
297 
298     /* ------------------------------------------------------------ */
299     /**
300      * @return Returns the acceptQueueSize.
301      */
302     public int getAcceptQueueSize()
303     {
304         return _acceptQueueSize;
305     }
306 
307     /* ------------------------------------------------------------ */
308     /**
309      * @param acceptQueueSize
310      *            The acceptQueueSize to set.
311      */
312     public void setAcceptQueueSize(int acceptQueueSize)
313     {
314         _acceptQueueSize = acceptQueueSize;
315     }
316 
317     /* ------------------------------------------------------------ */
318     /**
319      * @return Returns the number of acceptor threads.
320      */
321     public int getAcceptors()
322     {
323         return _acceptors;
324     }
325 
326     /* ------------------------------------------------------------ */
327     /**
328      * @param acceptors
329      *            The number of acceptor threads to set.
330      */
331     public void setAcceptors(int acceptors)
332     {
333         if (acceptors > 2*Runtime.getRuntime().availableProcessors())
334             Log.warn("Acceptors should be <=2*availableProcessors: "+this);
335         _acceptors = acceptors;
336     }
337 
338     /* ------------------------------------------------------------ */
339     /**
340      * @param soLingerTime
341      *            The soLingerTime to set or -1 to disable.
342      */
343     public void setSoLingerTime(int soLingerTime)
344     {
345         _soLingerTime = soLingerTime;
346     }
347 
348     /* ------------------------------------------------------------ */
349     @Override
350     protected void doStart() throws Exception
351     {
352         if (_server == null)
353             throw new IllegalStateException("No server");
354 
355         // open listener port
356         open();
357 
358         super.doStart();
359 
360         if (_threadPool == null)
361             _threadPool = _server.getThreadPool();
362         if (_threadPool != _server.getThreadPool() && (_threadPool instanceof LifeCycle))
363             ((LifeCycle)_threadPool).start();
364 
365         // Start selector thread
366         synchronized (this)
367         {
368             _acceptorThread = new Thread[getAcceptors()];
369 
370             for (int i = 0; i < _acceptorThread.length; i++)
371                 _threadPool.dispatch(new Acceptor(i));
372             if (_threadPool.isLowOnThreads())
373                 Log.warn("insufficient threads configured for {}",this);
374         }
375 
376         Log.info("Started {}",this);
377     }
378 
379     /* ------------------------------------------------------------ */
380     @Override
381     protected void doStop() throws Exception
382     {
383         try
384         {
385             close();
386         }
387         catch (IOException e)
388         {
389             Log.warn(e);
390         }
391 
392         if (_threadPool != _server.getThreadPool() && _threadPool instanceof LifeCycle)
393             ((LifeCycle)_threadPool).stop();
394 
395         super.doStop();
396 
397         Thread[] acceptors = null;
398         synchronized (this)
399         {
400             acceptors = _acceptorThread;
401             _acceptorThread = null;
402         }
403         if (acceptors != null)
404         {
405             for (int i = 0; i < acceptors.length; i++)
406             {
407                 Thread thread = acceptors[i];
408                 if (thread != null)
409                     thread.interrupt();
410             }
411         }
412     }
413 
414     /* ------------------------------------------------------------ */
415     public void join() throws InterruptedException
416     {
417         Thread[] threads = _acceptorThread;
418         if (threads != null)
419             for (int i = 0; i < threads.length; i++)
420                 if (threads[i] != null)
421                     threads[i].join();
422     }
423 
424     /* ------------------------------------------------------------ */
425     protected void configure(Socket socket) throws IOException
426     {
427         try
428         {
429             socket.setTcpNoDelay(true);
430             if (_soLingerTime >= 0)
431                 socket.setSoLinger(true,_soLingerTime / 1000);
432             else
433                 socket.setSoLinger(false,0);
434         }
435         catch (Exception e)
436         {
437             Log.ignore(e);
438         }
439     }
440 
441     /* ------------------------------------------------------------ */
442     public void customize(EndPoint endpoint, Request request) throws IOException
443     {
444         if (isForwarded())
445             checkForwardedHeaders(endpoint,request);
446     }
447 
448     /* ------------------------------------------------------------ */
449     protected void checkForwardedHeaders(EndPoint endpoint, Request request) throws IOException
450     {
451         HttpFields httpFields = request.getConnection().getRequestFields();
452 
453         // Retrieving headers from the request
454         String forwardedHost = getLeftMostValue(httpFields.getStringField(getForwardedHostHeader()));
455         String forwardedServer = getLeftMostValue(httpFields.getStringField(getForwardedServerHeader()));
456         String forwardedFor = getLeftMostValue(httpFields.getStringField(getForwardedForHeader()));
457         String forwardedProto = getLeftMostValue(httpFields.getStringField(getForwardedProtoHeader()));
458 
459         if (_hostHeader != null)
460         {
461             // Update host header
462             httpFields.put(HttpHeaders.HOST_BUFFER,_hostHeader);
463             request.setServerName(null);
464             request.setServerPort(-1);
465             request.getServerName();
466         }
467         else if (forwardedHost != null)
468         {
469             // Update host header
470             httpFields.put(HttpHeaders.HOST_BUFFER,forwardedHost);
471             request.setServerName(null);
472             request.setServerPort(-1);
473             request.getServerName();
474         }
475         else if (forwardedServer != null)
476         {
477             // Use provided server name
478             request.setServerName(forwardedServer);
479         }
480 
481         if (forwardedFor != null)
482         {
483             request.setRemoteAddr(forwardedFor);
484             InetAddress inetAddress = null;
485 
486             if (_useDNS)
487             {
488                 try
489                 {
490                     inetAddress = InetAddress.getByName(forwardedFor);
491                 }
492                 catch (UnknownHostException e)
493                 {
494                     Log.ignore(e);
495                 }
496             }
497 
498             request.setRemoteHost(inetAddress == null?forwardedFor:inetAddress.getHostName());
499         }
500         
501         if (forwardedProto != null)
502         {
503             request.setScheme(forwardedProto);
504         }
505     }
506 
507     /* ------------------------------------------------------------ */
508     protected String getLeftMostValue(String headerValue)
509     {
510         if (headerValue == null)
511             return null;
512 
513         int commaIndex = headerValue.indexOf(',');
514 
515         if (commaIndex == -1)
516         {
517             // Single value
518             return headerValue;
519         }
520 
521         // The left-most value is the farthest downstream client
522         return headerValue.substring(0,commaIndex);
523     }
524 
525     /* ------------------------------------------------------------ */
526     public void persist(EndPoint endpoint) throws IOException
527     {
528     }
529 
530     /* ------------------------------------------------------------ */
531     /*
532      * @see org.eclipse.jetty.server.Connector#getConfidentialPort()
533      */
534     public int getConfidentialPort()
535     {
536         return _confidentialPort;
537     }
538 
539     /* ------------------------------------------------------------ */
540     /* ------------------------------------------------------------ */
541     /*
542      * @see org.eclipse.jetty.server.Connector#getConfidentialScheme()
543      */
544     public String getConfidentialScheme()
545     {
546         return _confidentialScheme;
547     }
548 
549     /* ------------------------------------------------------------ */
550     /*
551      * @see
552      * org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server
553      * .Request)
554      */
555     public boolean isIntegral(Request request)
556     {
557         return false;
558     }
559 
560     /* ------------------------------------------------------------ */
561     /*
562      * @see org.eclipse.jetty.server.Connector#getConfidentialPort()
563      */
564     public int getIntegralPort()
565     {
566         return _integralPort;
567     }
568 
569     /* ------------------------------------------------------------ */
570     /*
571      * @see org.eclipse.jetty.server.Connector#getIntegralScheme()
572      */
573     public String getIntegralScheme()
574     {
575         return _integralScheme;
576     }
577 
578     /* ------------------------------------------------------------ */
579     /*
580      * @see org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server.Request)
581      */
582     public boolean isConfidential(Request request)
583     {
584         return _forwarded && request.getScheme().equalsIgnoreCase(HttpSchemes.HTTPS);
585     }
586 
587     /* ------------------------------------------------------------ */
588     /**
589      * @param confidentialPort
590      *            The confidentialPort to set.
591      */
592     public void setConfidentialPort(int confidentialPort)
593     {
594         _confidentialPort = confidentialPort;
595     }
596 
597     /* ------------------------------------------------------------ */
598     /**
599      * @param confidentialScheme
600      *            The confidentialScheme to set.
601      */
602     public void setConfidentialScheme(String confidentialScheme)
603     {
604         _confidentialScheme = confidentialScheme;
605     }
606 
607     /* ------------------------------------------------------------ */
608     /**
609      * @param integralPort
610      *            The integralPort to set.
611      */
612     public void setIntegralPort(int integralPort)
613     {
614         _integralPort = integralPort;
615     }
616 
617     /* ------------------------------------------------------------ */
618     /**
619      * @param integralScheme
620      *            The integralScheme to set.
621      */
622     public void setIntegralScheme(String integralScheme)
623     {
624         _integralScheme = integralScheme;
625     }
626 
627     /* ------------------------------------------------------------ */
628     protected abstract void accept(int acceptorID) throws IOException, InterruptedException;
629 
630     /* ------------------------------------------------------------ */
631     public void stopAccept(int acceptorID) throws Exception
632     {
633     }
634 
635     /* ------------------------------------------------------------ */
636     public boolean getResolveNames()
637     {
638         return _useDNS;
639     }
640 
641     /* ------------------------------------------------------------ */
642     public void setResolveNames(boolean resolve)
643     {
644         _useDNS = resolve;
645     }
646 
647     /* ------------------------------------------------------------ */
648     /**
649      * Is reverse proxy handling on?
650      * 
651      * @return true if this connector is checking the
652      *         x-forwarded-for/host/server headers
653      */
654     public boolean isForwarded()
655     {
656         return _forwarded;
657     }
658 
659     /* ------------------------------------------------------------ */
660     /**
661      * Set reverse proxy handling.
662      * If set to true, then the X-Forwarded headers (or the headers set in their place) 
663      * are looked for to set the request protocol, host, server and client ip.
664      * 
665      * @param check
666      *            true if this connector is checking the
667      *            x-forwarded-for/host/server headers
668      * @set {@link #setForwardedForHeader(String)}
669      * @set {@link #setForwardedHostHeader(String)}
670      * @set {@link #setForwardedProtoHeader(String)}
671      * @set {@link #setForwardedServerHeader(String)}
672      */
673     public void setForwarded(boolean check)
674     {
675         if (check)
676             Log.debug(this + " is forwarded");
677         _forwarded = check;
678     }
679 
680     /* ------------------------------------------------------------ */
681     public String getHostHeader()
682     {
683         return _hostHeader;
684     }
685 
686     /* ------------------------------------------------------------ */
687     /**
688      * Set a forced valued for the host header to control what is returned by
689      * {@link ServletRequest#getServerName()} and
690      * {@link ServletRequest#getServerPort()}. This value is only used if
691      * {@link #isForwarded()} is true.
692      * 
693      * @param hostHeader
694      *            The value of the host header to force.
695      */
696     public void setHostHeader(String hostHeader)
697     {
698         _hostHeader = hostHeader;
699     }
700 
701     /* ------------------------------------------------------------ */
702     /*
703      * 
704      * @see #setForwarded(boolean)
705      */
706     public String getForwardedHostHeader()
707     {
708         return _forwardedHostHeader;
709     }
710 
711     /* ------------------------------------------------------------ */
712     /**
713      * @param forwardedHostHeader
714      *            The header name for forwarded hosts (default x-forwarded-host)
715      * @see #setForwarded(boolean)
716      */
717     public void setForwardedHostHeader(String forwardedHostHeader)
718     {
719         _forwardedHostHeader = forwardedHostHeader;
720     }
721 
722     /* ------------------------------------------------------------ */
723     /**
724      * @return the header name for forwarded server.
725      * @see #setForwarded(boolean)
726      */
727     public String getForwardedServerHeader()
728     {
729         return _forwardedServerHeader;
730     }
731 
732     /* ------------------------------------------------------------ */
733     /**
734      * @param forwardedServerHeader
735      *            The header name for forwarded server (default
736      *            x-forwarded-server)
737      * @see #setForwarded(boolean)
738      */
739     public void setForwardedServerHeader(String forwardedServerHeader)
740     {
741         _forwardedServerHeader = forwardedServerHeader;
742     }
743 
744     /* ------------------------------------------------------------ */
745     /**
746      * @see #setForwarded(boolean)
747      */
748     public String getForwardedForHeader()
749     {
750         return _forwardedForHeader;
751     }
752 
753     /* ------------------------------------------------------------ */
754     /**
755      * @param forwardedRemoteAddressHeader
756      *            The header name for forwarded for (default x-forwarded-for)
757      * @see #setForwarded(boolean)
758      */
759     public void setForwardedForHeader(String forwardedRemoteAddressHeader)
760     {
761         _forwardedForHeader = forwardedRemoteAddressHeader;
762     }
763 
764     /* ------------------------------------------------------------ */
765     /** Get the forwardedProtoHeader.
766      * @return the forwardedProtoHeader (default X-Forwarded-For)
767      * @see #setForwarded(boolean)
768      */
769     public String getForwardedProtoHeader()
770     {
771         return _forwardedProtoHeader;
772     }
773 
774     /* ------------------------------------------------------------ */
775     /** Set the forwardedProtoHeader.
776      * @param forwardedProtoHeader the forwardedProtoHeader to set (default X-Forwarded-For)
777      * @see #setForwarded(boolean)
778      */
779     public void setForwardedProtoHeader(String forwardedProtoHeader)
780     {
781         _forwardedProtoHeader = forwardedProtoHeader;
782     }
783 
784     /* ------------------------------------------------------------ */
785     @Override
786     public String toString()
787     {
788         String name = this.getClass().getName();
789         int dot = name.lastIndexOf('.');
790         if (dot > 0)
791             name = name.substring(dot + 1);
792 
793         return name + "@" + (getHost() == null?"0.0.0.0":getHost()) + ":" + (getLocalPort() <= 0?getPort():getLocalPort());
794     }
795 
796     /* ------------------------------------------------------------ */
797     /* ------------------------------------------------------------ */
798     /* ------------------------------------------------------------ */
799     private class Acceptor implements Runnable
800     {
801         int _acceptor = 0;
802 
803         Acceptor(int id)
804         {
805             _acceptor = id;
806         }
807 
808         /* ------------------------------------------------------------ */
809         public void run()
810         {
811             Thread current = Thread.currentThread();
812             String name;
813             synchronized (AbstractConnector.this)
814             {
815                 if (_acceptorThread == null)
816                     return;
817 
818                 _acceptorThread[_acceptor] = current;
819                 name = _acceptorThread[_acceptor].getName();
820                 current.setName(name + " - Acceptor" + _acceptor + " " + AbstractConnector.this);
821             }
822             int old_priority = current.getPriority();
823 
824             try
825             {
826                 current.setPriority(old_priority - _acceptorPriorityOffset);
827                 while (isRunning() && getConnection() != null)
828                 {
829                     try
830                     {
831                         accept(_acceptor);
832                     }
833                     catch (EofException e)
834                     {
835                         Log.ignore(e);
836                     }
837                     catch (IOException e)
838                     {
839                         Log.ignore(e);
840                     }
841                     catch (InterruptedException x)
842                     {
843                         // Connector has been stopped
844                         Log.ignore(x);
845                     }
846                     catch (ThreadDeath e)
847                     {
848                         throw e;
849                     }
850                     catch (Throwable e)
851                     {
852                         Log.warn(e);
853                     }
854                 }
855             }
856             finally
857             {
858                 current.setPriority(old_priority);
859                 current.setName(name);
860 
861                 synchronized (AbstractConnector.this)
862                 {
863                     if (_acceptorThread != null)
864                         _acceptorThread[_acceptor] = null;
865                 }
866             }
867         }
868     }
869 
870     /* ------------------------------------------------------------ */
871     public String getName()
872     {
873         if (_name == null)
874             _name = (getHost() == null?"0.0.0.0":getHost()) + ":" + (getLocalPort() <= 0?getPort():getLocalPort());
875         return _name;
876     }
877 
878     /* ------------------------------------------------------------ */
879     public void setName(String name)
880     {
881         _name = name;
882     }
883 
884     /* ------------------------------------------------------------ */
885     /**
886      * @return Get the number of requests handled by this connector since last
887      *         call of statsReset(). If setStatsOn(false) then this is
888      *         undefined.
889      */
890     public int getRequests()
891     {
892         return (int)_requestStats.getTotal();
893     }
894 
895     /* ------------------------------------------------------------ */
896     /**
897      * @return Returns the connectionsDurationTotal.
898      */
899     public long getConnectionsDurationTotal()
900     {
901         return _connectionDurationStats.getTotal();
902     }
903 
904     /* ------------------------------------------------------------ */
905     /**
906      * @return Number of connections accepted by the server since statsReset()
907      *         called. Undefined if setStatsOn(false).
908      */
909     public int getConnections()
910     {
911         return (int)_connectionStats.getTotal();
912     }
913 
914     /* ------------------------------------------------------------ */
915     /**
916      * @return Number of connections currently open that were opened since
917      *         statsReset() called. Undefined if setStatsOn(false).
918      */
919     public int getConnectionsOpen()
920     {
921         return (int)_connectionStats.getCurrent();
922     }
923 
924     /* ------------------------------------------------------------ */
925     /**
926      * @return Maximum number of connections opened simultaneously since
927      *         statsReset() called. Undefined if setStatsOn(false).
928      */
929     public int getConnectionsOpenMax()
930     {
931         return (int)_connectionStats.getMax();
932     }
933 
934     /* ------------------------------------------------------------ */
935     /**
936      * @return Mean duration in milliseconds of open connections since
937      *         statsReset() called. Undefined if setStatsOn(false).
938      */
939     public double getConnectionsDurationMean()
940     {
941         return _connectionDurationStats.getMean();
942     }
943 
944     /* ------------------------------------------------------------ */
945     /**
946      * @return Maximum duration in milliseconds of an open connection since
947      *         statsReset() called. Undefined if setStatsOn(false).
948      */
949     public long getConnectionsDurationMax()
950     {
951         return _connectionDurationStats.getMax();
952     }
953 
954     /* ------------------------------------------------------------ */
955     /**
956      * @return Standard deviation of duration in milliseconds of open
957      *         connections since statsReset() called. Undefined if
958      *         setStatsOn(false).
959      */
960     public double getConnectionsDurationStdDev()
961     {
962         return _connectionDurationStats.getStdDev();
963     }
964 
965     /* ------------------------------------------------------------ */
966     /**
967      * @return Mean number of requests per connection since statsReset() called.
968      *         Undefined if setStatsOn(false).
969      */
970     public double getConnectionsRequestsMean()
971     {
972         return _requestStats.getMean();
973     }
974 
975     /* ------------------------------------------------------------ */
976     /**
977      * @return Maximum number of requests per connection since statsReset()
978      *         called. Undefined if setStatsOn(false).
979      */
980     public int getConnectionsRequestsMax()
981     {
982         return (int)_requestStats.getMax();
983     }
984 
985     /* ------------------------------------------------------------ */
986     /**
987      * @return Standard deviation of number of requests per connection since
988      *         statsReset() called. Undefined if setStatsOn(false).
989      */
990     public double getConnectionsRequestsStdDev()
991     {
992         return _requestStats.getStdDev();
993     }
994 
995     /* ------------------------------------------------------------ */
996     /**
997      * Reset statistics.
998      */
999     public void statsReset()
1000     {
1001         updateNotEqual(_statsStartedAt,-1,System.currentTimeMillis());
1002 
1003         _requestStats.reset();
1004         _connectionStats.reset();
1005         _connectionDurationStats.reset();
1006     }
1007 
1008     /* ------------------------------------------------------------ */
1009     public void setStatsOn(boolean on)
1010     {
1011         if (on && _statsStartedAt.get() != -1)
1012             return;
1013 
1014         Log.debug("Statistics on = " + on + " for " + this);
1015 
1016         statsReset();
1017         _statsStartedAt.set(on?System.currentTimeMillis():-1);
1018     }
1019 
1020     /* ------------------------------------------------------------ */
1021     /**
1022      * @return True if statistics collection is turned on.
1023      */
1024     public boolean getStatsOn()
1025     {
1026         return _statsStartedAt.get() != -1;
1027     }
1028 
1029     /* ------------------------------------------------------------ */
1030     /**
1031      * @return Timestamp stats were started at.
1032      */
1033     public long getStatsOnMs()
1034     {
1035         long start = _statsStartedAt.get();
1036 
1037         return (start != -1)?(System.currentTimeMillis() - start):0;
1038     }
1039 
1040     /* ------------------------------------------------------------ */
1041     protected void connectionOpened(Connection connection)
1042     {
1043         if (_statsStartedAt.get() == -1)
1044             return;
1045 
1046         _connectionStats.increment();
1047     }
1048 
1049     /* ------------------------------------------------------------ */
1050     protected void connectionUpgraded(Connection oldConnection, Connection newConnection)
1051     {
1052         _requestStats.set((oldConnection instanceof HttpConnection)?((HttpConnection)oldConnection).getRequests():0);
1053     }
1054 
1055     /* ------------------------------------------------------------ */
1056     protected void connectionClosed(Connection connection)
1057     {
1058         connection.closed();
1059 
1060         if (_statsStartedAt.get() == -1)
1061             return;
1062 
1063         long duration = System.currentTimeMillis() - connection.getTimeStamp();
1064         int requests = (connection instanceof HttpConnection)?((HttpConnection)connection).getRequests():0;
1065         _requestStats.set(requests);
1066         _connectionStats.decrement();
1067         _connectionDurationStats.set(duration);
1068     }
1069 
1070     /* ------------------------------------------------------------ */
1071     /**
1072      * @return the acceptorPriority
1073      */
1074     public int getAcceptorPriorityOffset()
1075     {
1076         return _acceptorPriorityOffset;
1077     }
1078 
1079     /* ------------------------------------------------------------ */
1080     /**
1081      * Set the priority offset of the acceptor threads. The priority is adjusted
1082      * by this amount (default 0) to either favour the acceptance of new threads
1083      * and newly active connections or to favour the handling of already
1084      * dispatched connections.
1085      * 
1086      * @param offset
1087      *            the amount to alter the priority of the acceptor threads.
1088      */
1089     public void setAcceptorPriorityOffset(int offset)
1090     {
1091         _acceptorPriorityOffset = offset;
1092     }
1093 
1094     /* ------------------------------------------------------------ */
1095     /**
1096      * @return True if the the server socket will be opened in SO_REUSEADDR
1097      *         mode.
1098      */
1099     public boolean getReuseAddress()
1100     {
1101         return _reuseAddress;
1102     }
1103 
1104     /* ------------------------------------------------------------ */
1105     /**
1106      * @param reuseAddress
1107      *            True if the the server socket will be opened in SO_REUSEADDR
1108      *            mode.
1109      */
1110     public void setReuseAddress(boolean reuseAddress)
1111     {
1112         _reuseAddress = reuseAddress;
1113     }
1114 
1115     /* ------------------------------------------------------------ */
1116     public boolean isLowResources()
1117     {
1118         if (_threadPool != null)
1119             return _threadPool.isLowOnThreads();
1120         return _server.getThreadPool().isLowOnThreads();
1121     }
1122 
1123     /* ------------------------------------------------------------ */
1124     private void updateNotEqual(AtomicLong valueHolder, long compare, long value)
1125     {
1126         long oldValue = valueHolder.get();
1127         while (compare != oldValue)
1128         {
1129             if (valueHolder.compareAndSet(oldValue,value))
1130                 break;
1131             oldValue = valueHolder.get();
1132         }
1133     }
1134 
1135     /* ------------------------------------------------------------ */
1136     public String dump()
1137     {
1138         return AggregateLifeCycle.dump(this);
1139     }
1140 
1141     /* ------------------------------------------------------------ */
1142     public void dump(Appendable out, String indent) throws IOException
1143     {
1144         out.append(String.valueOf(this)).append("\n");
1145     }
1146     
1147     
1148 }