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