]> WPIA git - gigi.git/blobdiff - 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
diff --git a/lib/jetty/org/eclipse/jetty/io/IdleTimeout.java b/lib/jetty/org/eclipse/jetty/io/IdleTimeout.java
new file mode 100644 (file)
index 0000000..8b251ac
--- /dev/null
@@ -0,0 +1,182 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+//  ------------------------------------------------------------------------
+//  All rights reserved. This program and the accompanying materials
+//  are made available under the terms of the Eclipse Public License v1.0
+//  and Apache License v2.0 which accompanies this distribution.
+//
+//      The Eclipse Public License is available at
+//      http://www.eclipse.org/legal/epl-v10.html
+//
+//      The Apache License v2.0 is available at
+//      http://www.opensource.org/licenses/apache2.0.php
+//
+//  You may elect to redistribute this code under either of these licenses.
+//  ========================================================================
+//
+
+package org.eclipse.jetty.io;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.thread.Scheduler;
+
+/**
+ * An Abstract implementation of an Idle Timeout.
+ * <p/>
+ * This implementation is optimised that timeout operations are not cancelled on
+ * every operation. Rather timeout are allowed to expire and a check is then made
+ * to see when the last operation took place.  If the idle timeout has not expired,
+ * the timeout is rescheduled for the earliest possible time a timeout could occur.
+ */
+public abstract class IdleTimeout
+{
+    private static final Logger LOG = Log.getLogger(IdleTimeout.class);
+    private final Scheduler _scheduler;
+    private final AtomicReference<Scheduler.Task> _timeout = new AtomicReference<>();
+    private volatile long _idleTimeout;
+    private volatile long _idleTimestamp = System.currentTimeMillis();
+
+    private final Runnable _idleTask = new Runnable()
+    {
+        @Override
+        public void run()
+        {
+            long idleLeft = checkIdleTimeout();
+            if (idleLeft >= 0)
+                scheduleIdleTimeout(idleLeft > 0 ? idleLeft : getIdleTimeout());
+        }
+    };
+
+    /**
+     * @param scheduler A scheduler used to schedule checks for the idle timeout.
+     */
+    public IdleTimeout(Scheduler scheduler)
+    {
+        _scheduler = scheduler;
+    }
+
+    public long getIdleTimestamp()
+    {
+        return _idleTimestamp;
+    }
+
+    public long getIdleTimeout()
+    {
+        return _idleTimeout;
+    }
+
+    public void setIdleTimeout(long idleTimeout)
+    {
+        long old = _idleTimeout;
+        _idleTimeout = idleTimeout;
+
+        // Do we have an old timeout
+        if (old > 0)
+        {
+            // if the old was less than or equal to the new timeout, then nothing more to do
+            if (old <= idleTimeout)
+                return;
+
+            // old timeout is too long, so cancel it.
+            deactivate();
+        }
+
+        // If we have a new timeout, then check and reschedule
+        if (isOpen())
+            activate();
+    }
+
+    /**
+     * This method should be called when non-idle activity has taken place.
+     */
+    public void notIdle()
+    {
+        _idleTimestamp = System.currentTimeMillis();
+    }
+
+    private void scheduleIdleTimeout(long delay)
+    {
+        Scheduler.Task newTimeout = null;
+        if (isOpen() && delay > 0 && _scheduler != null)
+            newTimeout = _scheduler.schedule(_idleTask, delay, TimeUnit.MILLISECONDS);
+        Scheduler.Task oldTimeout = _timeout.getAndSet(newTimeout);
+        if (oldTimeout != null)
+            oldTimeout.cancel();
+    }
+
+    public void onOpen()
+    {
+        activate();
+    }
+
+    private void activate()
+    {
+        if (_idleTimeout > 0)
+            _idleTask.run();
+    }
+
+    public void onClose()
+    {
+        deactivate();
+    }
+
+    private void deactivate()
+    {
+        Scheduler.Task oldTimeout = _timeout.getAndSet(null);
+        if (oldTimeout != null)
+            oldTimeout.cancel();
+    }
+
+    protected long checkIdleTimeout()
+    {
+        if (isOpen())
+        {
+            long idleTimestamp = getIdleTimestamp();
+            long idleTimeout = getIdleTimeout();
+            long idleElapsed = System.currentTimeMillis() - idleTimestamp;
+            long idleLeft = idleTimeout - idleElapsed;
+
+            LOG.debug("{} idle timeout check, elapsed: {} ms, remaining: {} ms", this, idleElapsed, idleLeft);
+
+            if (idleTimestamp != 0 && idleTimeout > 0)
+            {
+                if (idleLeft <= 0)
+                {
+                    LOG.debug("{} idle timeout expired", this);
+                    try
+                    {
+                        onIdleExpired(new TimeoutException("Idle timeout expired: " + idleElapsed + "/" + idleTimeout + " ms"));
+                    }
+                    finally
+                    {
+                        notIdle();
+                    }
+                }
+            }
+
+            return idleLeft >= 0 ? idleLeft : 0;
+        }
+        return -1;
+    }
+
+    /**
+     * This abstract method is called when the idle timeout has expired.
+     *
+     * @param timeout a TimeoutException
+     */
+    protected abstract void onIdleExpired(TimeoutException timeout);
+
+    /**
+     * This abstract method should be called to check if idle timeouts
+     * should still be checked.
+     *
+     * @return True if the entity monitored should still be checked for idle timeouts
+     */
+    public abstract boolean isOpen();
+}