//
//  ========================================================================
//  Copyright (c) 1995-2019 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.

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.

/** * 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.nanoTime();
Params:
  • scheduler – A scheduler used to schedule checks for the idle timeout.
/** * @param scheduler A scheduler used to schedule checks for the idle timeout. */
public IdleTimeout(Scheduler scheduler) { _scheduler = scheduler; } public Scheduler getScheduler() { return _scheduler; }
Returns:the period of time, in milliseconds, that this object was idle
/** * @return the period of time, in milliseconds, that this object was idle */
public long getIdleFor() { return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - _idleTimestamp); }
See Also:
Returns:the idle timeout in milliseconds
/** * @return the idle timeout in milliseconds * @see #setIdleTimeout(long) */
public long getIdleTimeout() { return _idleTimeout; }

Sets the idle timeout in milliseconds.

A value that is less than or zero disables the idle timeout checks.

Params:
  • idleTimeout – the idle timeout in milliseconds
See Also:
/** * <p>Sets the idle timeout in milliseconds.</p> * <p>A value that is less than or zero disables the idle timeout checks.</p> * * @param idleTimeout the idle timeout in milliseconds * @see #getIdleTimeout() */
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.
/** * This method should be called when non-idle activity has taken place. */
public void notIdle() { _idleTimestamp = System.nanoTime(); } private void idleCheck() { long idleLeft = checkIdleTimeout(); if (idleLeft >= 0) scheduleIdleTimeout(idleLeft > 0 ? idleLeft : getIdleTimeout()); } private void scheduleIdleTimeout(long delay) { Scheduler.Task newTimeout = null; if (isOpen() && delay > 0 && _scheduler != null) newTimeout = _scheduler.schedule(this::idleCheck, delay, TimeUnit.MILLISECONDS); Scheduler.Task oldTimeout = _timeout.getAndSet(newTimeout); if (oldTimeout != null) oldTimeout.cancel(); } public void onOpen() { activate(); } private void activate() { if (_idleTimeout > 0) idleCheck(); } 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 = _idleTimestamp; long idleElapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - idleTimestamp); long idleTimeout = getIdleTimeout(); long idleLeft = idleTimeout - idleElapsed; if (LOG.isDebugEnabled()) LOG.debug("{} idle timeout check, elapsed: {} ms, remaining: {} ms", this, idleElapsed, idleLeft); if (idleTimeout > 0) { if (idleLeft <= 0) { if (LOG.isDebugEnabled()) 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.
Params:
  • timeout – a TimeoutException
/** * 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.
Returns:True if the entity monitored should still be checked for idle timeouts
/** * 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(); }