]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/io/IdleTimeout.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / io / IdleTimeout.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 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.io;
20
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.TimeoutException;
23 import java.util.concurrent.atomic.AtomicReference;
24
25 import org.eclipse.jetty.util.log.Log;
26 import org.eclipse.jetty.util.log.Logger;
27 import org.eclipse.jetty.util.thread.Scheduler;
28
29 /**
30  * An Abstract implementation of an Idle Timeout.
31  * <p/>
32  * This implementation is optimised that timeout operations are not cancelled on
33  * every operation. Rather timeout are allowed to expire and a check is then made
34  * to see when the last operation took place.  If the idle timeout has not expired,
35  * the timeout is rescheduled for the earliest possible time a timeout could occur.
36  */
37 public abstract class IdleTimeout
38 {
39     private static final Logger LOG = Log.getLogger(IdleTimeout.class);
40     private final Scheduler _scheduler;
41     private final AtomicReference<Scheduler.Task> _timeout = new AtomicReference<>();
42     private volatile long _idleTimeout;
43     private volatile long _idleTimestamp = System.currentTimeMillis();
44
45     private final Runnable _idleTask = new Runnable()
46     {
47         @Override
48         public void run()
49         {
50             long idleLeft = checkIdleTimeout();
51             if (idleLeft >= 0)
52                 scheduleIdleTimeout(idleLeft > 0 ? idleLeft : getIdleTimeout());
53         }
54     };
55
56     /**
57      * @param scheduler A scheduler used to schedule checks for the idle timeout.
58      */
59     public IdleTimeout(Scheduler scheduler)
60     {
61         _scheduler = scheduler;
62     }
63
64     public long getIdleTimestamp()
65     {
66         return _idleTimestamp;
67     }
68
69     public long getIdleTimeout()
70     {
71         return _idleTimeout;
72     }
73
74     public void setIdleTimeout(long idleTimeout)
75     {
76         long old = _idleTimeout;
77         _idleTimeout = idleTimeout;
78
79         // Do we have an old timeout
80         if (old > 0)
81         {
82             // if the old was less than or equal to the new timeout, then nothing more to do
83             if (old <= idleTimeout)
84                 return;
85
86             // old timeout is too long, so cancel it.
87             deactivate();
88         }
89
90         // If we have a new timeout, then check and reschedule
91         if (isOpen())
92             activate();
93     }
94
95     /**
96      * This method should be called when non-idle activity has taken place.
97      */
98     public void notIdle()
99     {
100         _idleTimestamp = System.currentTimeMillis();
101     }
102
103     private void scheduleIdleTimeout(long delay)
104     {
105         Scheduler.Task newTimeout = null;
106         if (isOpen() && delay > 0 && _scheduler != null)
107             newTimeout = _scheduler.schedule(_idleTask, delay, TimeUnit.MILLISECONDS);
108         Scheduler.Task oldTimeout = _timeout.getAndSet(newTimeout);
109         if (oldTimeout != null)
110             oldTimeout.cancel();
111     }
112
113     public void onOpen()
114     {
115         activate();
116     }
117
118     private void activate()
119     {
120         if (_idleTimeout > 0)
121             _idleTask.run();
122     }
123
124     public void onClose()
125     {
126         deactivate();
127     }
128
129     private void deactivate()
130     {
131         Scheduler.Task oldTimeout = _timeout.getAndSet(null);
132         if (oldTimeout != null)
133             oldTimeout.cancel();
134     }
135
136     protected long checkIdleTimeout()
137     {
138         if (isOpen())
139         {
140             long idleTimestamp = getIdleTimestamp();
141             long idleTimeout = getIdleTimeout();
142             long idleElapsed = System.currentTimeMillis() - idleTimestamp;
143             long idleLeft = idleTimeout - idleElapsed;
144
145             LOG.debug("{} idle timeout check, elapsed: {} ms, remaining: {} ms", this, idleElapsed, idleLeft);
146
147             if (idleTimestamp != 0 && idleTimeout > 0)
148             {
149                 if (idleLeft <= 0)
150                 {
151                     LOG.debug("{} idle timeout expired", this);
152                     try
153                     {
154                         onIdleExpired(new TimeoutException("Idle timeout expired: " + idleElapsed + "/" + idleTimeout + " ms"));
155                     }
156                     finally
157                     {
158                         notIdle();
159                     }
160                 }
161             }
162
163             return idleLeft >= 0 ? idleLeft : 0;
164         }
165         return -1;
166     }
167
168     /**
169      * This abstract method is called when the idle timeout has expired.
170      *
171      * @param timeout a TimeoutException
172      */
173     protected abstract void onIdleExpired(TimeoutException timeout);
174
175     /**
176      * This abstract method should be called to check if idle timeouts
177      * should still be checked.
178      *
179      * @return True if the entity monitored should still be checked for idle timeouts
180      */
181     public abstract boolean isOpen();
182 }