All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.crankuptheamps.client.ExponentialDelayStrategy Maven / Gradle / Ivy

////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2010-2021 60East Technologies Inc., All Rights Reserved.
//
// This computer software is owned by 60East Technologies Inc. and is
// protected by U.S. copyright laws and other laws and by international
// treaties.  This computer software is furnished by 60East Technologies
// Inc. pursuant to a written license agreement and may be used, copied,
// transmitted, and stored only in accordance with the terms of such
// license agreement and with the inclusion of the above copyright notice.
// This computer software or any other copies thereof may not be provided
// or otherwise made available to any other person.
//
// U.S. Government Restricted Rights.  This computer software: (a) was
// developed at private expense and is in all respects the proprietary
// information of 60East Technologies Inc.; (b) was not developed with
// government funds; (c) is a trade secret of 60East Technologies Inc.
// for all purposes of the Freedom of Information Act; and (d) is a
// commercial item and thus, pursuant to Section 12.212 of the Federal
// Acquisition Regulations (FAR) and DFAR Supplement Section 227.7202,
// Government's use, duplication or disclosure of the computer software
// is subject to the restrictions set forth by 60East Technologies Inc..
//
////////////////////////////////////////////////////////////////////////////
package com.crankuptheamps.client;
import com.crankuptheamps.client.exception.*;

/**
 * ExponentialDelayStrategy is an implementation that exponentially
 * "backs off" when reconnecting to the same server, with a maximum
 * number of retries before it gives up entirely.
 */
public class ExponentialDelayStrategy implements ReconnectDelayStrategy
{
    /**
     * A specialized exception type thrown by ExponentialDelayStrategy
     * to indicate that the client should "give up" on attempting to
     * reconnect to a server.
     */
    public static class MaximumRetryExceeded extends AMPSException
    {
        private static final long serialVersionUID = 1L;
    }
    /**
     * The default time (in milliseconds) to wait before reconnecting to a
     * server for the first time after a failed connection.
     */
    public static final int DEFAULT_DELAY = 200;

    /**
     * The default maximum time to wait between any reconnection attempts.
     * Exponential backoff will not exceed this maximum.
     */
    public static final int DEFAULT_MAXIMUM_DELAY = 20 * 1000;

    /**
     * The default exponent to use for calculating the next delay time. For
     * example if the initial time is 200ms and the exponent is 2.0, the next
     * delay will be 400ms, then 800ms, etc.
     */
    public static final double DEFAULT_BACKOFF_EXPONENT = 2.0;

    /**
     * The default time (in millseconds) to allow reconnect attempts to
     * continue without a successful connection, before "giving up" and
     * abandoning the connection attempt. 0 means never give up.
     */
    public static final int DEFAULT_MAXIMUM_RETRY_TIME = 0;

    /**
     * The amount of 'jitter' to apply when calculating a delay time,
     * measured in multiples of the initial delay. Jitter is used to reduce
     * the number of simultaneous reconnects that may be issued from multiple
     * clients.
     */
    public static final double DEFAULT_JITTER = 1.0;

    /**
     * Constructs an ExponentialDelayStrategy, the default strategy for
     * HAClient, using the default values for the class.
     *
     * For example, when constructed, the new object will use the
     * {@link #DEFAULT_DELAY} ({@value #DEFAULT_DELAY} ms)
     * as the initial delay, the {@link #DEFAULT_BACKOFF_EXPONENT} ({@value #DEFAULT_BACKOFF_EXPONENT})
     * as the backoff exponent, the  {@link #DEFAULT_MAXIMUM_RETRY_TIME} 
     * ({@value #DEFAULT_MAXIMUM_RETRY_TIME} -- never time out) as the maximum
     * retry time, and so on. You can use the setters provided on the class to
     * adjust these settings.
     */
    public ExponentialDelayStrategy()
    {
        _initialDelay     = DEFAULT_DELAY;
        _maximumDelay     = DEFAULT_MAXIMUM_DELAY;
        _backoffExponent  = DEFAULT_BACKOFF_EXPONENT;
        _maximumRetryTime = DEFAULT_MAXIMUM_RETRY_TIME;
        _jitter           = DEFAULT_JITTER;
        _currentDelay     = _initialDelay;
        _startTime        = 0;
    }

    /**
     * Sets the time (in milliseconds) to wait before reconnecting to
     * a server for the first time after a failed connection.
     * @param initialDelay_ The new initial delay value, in milliseconds.
     * @return Self.
     */
    public ExponentialDelayStrategy setInitialDelay(int initialDelay_)
    {
        _initialDelay = initialDelay_;
        _currentDelay = initialDelay_;
        return this;
    }

    /**
     * Sets the maximum time to wait between any reconnection attempts.
     * Exponential backoff will not exceed this maximum.
     * @param maximumDelay_ The new maximum delay value, in milliseconds.
     * @return Self.
     */
    public ExponentialDelayStrategy setMaximumDelay(int maximumDelay_)
    {
        _maximumDelay = maximumDelay_;
        return this;
    }

    /**
     * Sets the exponent to use for calculating the next delay time. For
     * example if the initial time is 200ms and the exponent is 2.0, the next
     * delay will be 400ms, then 800ms, etc.
     * @param exponent_ The new exponent value.
     * @return Self.
     */
    public ExponentialDelayStrategy setBackoffExponent(double exponent_)
    {
        _backoffExponent = exponent_;
        return this;
    }

    /**
     * Sets the time (in millseconds) to allow reconnect attempts to
     * continue without a successful connection, before "giving up" and
     * abandoning the connection attempt. 0 means never give up.
     * @param maximumRetryTime_ The new maximum retry time, in milliseconds.
     * @return Self.
     */
    public ExponentialDelayStrategy setMaximumRetryTime(
            int maximumRetryTime_)
    {
        _maximumRetryTime = maximumRetryTime_;
        return this;
    }

    /**
     * Sets the jitter factor used to add randomness to the delay time.
     * Jitter is represented as a multiple of the initial delay time;
     * a random number from [0, (JITTER * INITIAL_DELAY) ) is added
     * to nonzero time delays.
     * 
     * @param jitter_ The jitter factor, in multiples of the initial delay.
     * @return self.
     */
    public ExponentialDelayStrategy setJitter(double jitter_)
    {
        _jitter = jitter_;
        return this;
    }

    public int getConnectWaitDuration(String uri_) throws Exception
    {
        _throwIfMaximumExceeded();
        if(_lastUri == null || !_lastUri.equals(uri_))
        {
            _lastUri = uri_;
            if(_firstUri != null && _firstUri.equals(uri_))
            {
                // we've wrapped around the "list." Delay.
                return _currentDurationAndIncrease();
            }
            else if(_firstUri == null)
            {
                _firstUri = uri_;
            }
            return 0;
        }
        return _currentDurationAndIncrease();
    }

    public void reset()
    {
        _lastUri      = null;
        _firstUri     = null;
        _currentDelay = _initialDelay;
        _startTime    = 0;
    }

    /**
     * An internal function used to check if the configured retry has been
     * exceeded, and if so, throw MaximumRetryExceeded to cause the calling
     * HAClient to give up on reconnecting to a server.
     *
     * @throws MaximumRetryExceeded The configured maximum retry time has
     *                              been exceeded.
     */
    protected void _throwIfMaximumExceeded() throws MaximumRetryExceeded
    {
        if(_maximumRetryTime > 0)
        {
            long now = System.currentTimeMillis();
            if(_startTime > 0)
            {
                if(now - _startTime > _maximumRetryTime)
                {
                    throw new MaximumRetryExceeded();
                }

            }
            else
            {
                _startTime = now;
            }
        }
    }

    /**
     * Returns the _currentDelay, and also expands _currentDelay based
     * on the configured exponent and maximum.
     * @return The delay that should be used, prior to increasing it.
     * @throws MaximumRetryExceeded The configured maximum retry time has
     *                              been exceeded.
     */
    protected int _currentDurationAndIncrease() throws MaximumRetryExceeded
    {
        int newDelay = (int) (_currentDelay * _backoffExponent);
        if(newDelay > _maximumDelay) newDelay = _maximumDelay;
        // Don't exceed _maximumDelay ...
        int maxJitter = (int) (_jitter * _initialDelay);
        // but don't go negative due to large jitter
        if (maxJitter > _maximumDelay) maxJitter = _maximumDelay;
        int currentDelay = _currentDelay;
        if (_jitter > 0.0)
        {
            // Don't exceed _maximumDelay, could happen with large jitter
            if(currentDelay > _maximumDelay - maxJitter)
                currentDelay = (_maximumDelay - maxJitter > _initialDelay) ?
                                _maximumDelay - maxJitter : _initialDelay;
            currentDelay += (int) (Math.random() * _jitter * _initialDelay);
            if (currentDelay > _maximumDelay) currentDelay = _maximumDelay;
        }

        if(_maximumRetryTime > 0)
        {
            int timeElapsed = (int) (System.currentTimeMillis() - _startTime);
            if(timeElapsed + currentDelay > _maximumRetryTime)
            {
                if(timeElapsed > _maximumRetryTime)
                    throw new MaximumRetryExceeded();
                currentDelay = _maximumRetryTime - timeElapsed;
            }

        }
        _currentDelay = newDelay;
        return currentDelay;
    }

    protected int     _initialDelay;
    protected int     _maximumDelay;
    protected double  _backoffExponent;
    protected int     _maximumRetryTime;
    protected double  _jitter;
    protected int     _currentDelay;
    protected String  _lastUri;
    protected String  _firstUri;
    protected long    _startTime;
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy