This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* ____ __ | |
** / __ \ / /_ ____ | |
** / /_/ // __ \ / __ \ © 2009 H&S Information Systems | |
** / _, _// / / // /_/ / ALL RIGHTS RESERVED | |
** /_/ |_|/_/ /_/ \____/ | |
*/ | |
/* | |
Limiter.scala | |
Copyright © 2009 H&S Information Systems. ALL RIGHTS RESERVED | |
$Id$ | |
*/ | |
package rrs.util | |
import java.lang.System.currentTimeMillis | |
/** | |
class Limiter: Resource consumption limiter | |
@param timeLimit Elapsed time limit in seconds | |
@param opLimit Operation count limit | |
@param signal A non-returning function accepting this '''Limiter''' called when a limit is exceeded | |
@param startNow Start the timer upon constuction of this instance. | |
@param autoStart Start the timer when the first operation is recorded. | |
*/ | |
class Limiter(val timeLimit: Float, | |
val opLimit: Long, | |
signal: Option[(Limiter => Nothing)], | |
startNow: Boolean, | |
autoStart: Boolean) | |
{ | |
private var opCounter = 0L | |
private var startTime = 0L | |
private var lastCountTime = 0L | |
private val msLimit = (timeLimit * 1000).toLong | |
/** Start the timer */ | |
def | |
startTimer: Limiter = { | |
startTime = currentTimeMillis | |
this | |
} | |
/** Reset operation counter and timer, put the timer in its "stopped" state */ | |
def | |
reset: Limiter = { | |
opCounter = 0L | |
startTime = 0L | |
lastCountTime = 0L | |
this | |
} | |
/** | |
Count another operation and note the elapsed time; | |
If no limit is exceeded return '''true''' otherwise | |
throw signal if requested or return '''false''' if not | |
*/ | |
def | |
op: Boolean = { | |
if (startTime == 0 && autoStart) | |
startTimer | |
if (startTime != 0) | |
lastCountTime = currentTimeMillis | |
opCounter += 1 | |
if (opLimit > 0 && opCounter > opLimit | |
|| timeLimit > 0 && startTime > 0 && (lastCountTime - startTime > msLimit)) | |
if (signal.isDefined) | |
signal.get.apply(this) | |
else | |
false | |
else | |
true | |
} | |
/** Ascertain the current operation count */ | |
def | |
opCount: Long = | |
opCounter | |
/** Ascertain whether the operation count has been exceeded */ | |
def | |
countExceeded: Boolean = | |
opLimit > 0 && opCounter > opLimit | |
/** Ascertain the time elapsed between staring the time and the most recent operation */ | |
def | |
timeElapsed: Float = | |
(lastCountTime - startTime) / 1000.0f | |
/** Ascertain whether the most recently counted operation occurred at a time in excess of the limit */ | |
def | |
timeExceeded: Boolean = | |
timeLimit > 0 && startTime > 0 && (lastCountTime - startTime > timeLimit) | |
} | |
/** | |
class LimiteBuilder: A helper for constructing '''Limiter''' instances a piece at a time. | |
*/ | |
class LimiterBuilder | |
{ | |
private var aStart = false | |
private var sNow = false | |
private var tLimit = 0.0f | |
private var oLimit = 0L | |
private var sig: Option[(Limiter => Nothing)] = None | |
/** Make the '''Limiter''' start its timer automatically upon the first call to '''Limiter.op''' */ | |
def | |
autoStart: LimiterBuilder = { | |
aStart = true | |
this | |
} | |
/** Make the '''Limiter''' auto-start its timer */ | |
def | |
startNow: LimiterBuilder = { | |
sNow = true | |
this | |
} | |
/** Establish the '''Limiter''''s time limit */ | |
def | |
timeLimit(limit: Float): LimiterBuilder = { | |
tLimit = limit | |
this | |
} | |
/** Establish the '''Limiter''''s time limit */ | |
def | |
timeLimit(limit: Double): LimiterBuilder = { | |
tLimit = limit.toFloat | |
this | |
} | |
/** Establish the '''Limiter''''s operation limit */ | |
def | |
opLimit(limit: Long): LimiterBuilder = { | |
oLimit = limit | |
this | |
} | |
/** | |
Make the '''Limiter''' use ''sigGen'' to synthesize | |
the signal that will be thrown when a limit is exceeded | |
*/ | |
def | |
signal(sigGen: Limiter => Nothing): LimiterBuilder = { | |
sig = Some(sigGen) | |
this | |
} | |
/** Make the '''Limiter''' use the standard signal generator, '''Limiter'''.signalF */ | |
def | |
signal: LimiterBuilder = { | |
sig = Some(Limiter.signalF) | |
this | |
} | |
/** Create a '''Limiter''' using the current parameters of this '''LimiterBuilder''' */ | |
def | |
limiter: Limiter = | |
new Limiter(tLimit, oLimit, sig, sNow, aStart) | |
} | |
object Limiter | |
{ | |
/** Create a new '''LimiterBuilder''' */ | |
def | |
build: LimiterBuilder = | |
new LimiterBuilder | |
/** Synthesize an instance of (a subtype of) '''LimitExceeded''' suitable to the current state of ''limiter'' */ | |
def | |
signaler(limiter: Limiter): LimitExceeded = | |
if (limiter.timeExceeded) new TimeLimitExceeded(limiter) | |
else if (limiter.countExceeded) new OpLimitExceeded(limiter) | |
else new NoLimitExceeded(limiter) | |
/** The default signal throwing method */ | |
def | |
signal(limiter: Limiter): Nothing = | |
throw signaler(limiter) | |
/** The default signal throwing function */ | |
val | |
signalF = signal _ | |
} | |
/** | |
abstract class LimitExceeded: Exceptions used to signal exceeding a resource limit | |
*/ | |
abstract | |
class LimitExceeded(val limiter: Limiter, message: String) | |
extends Exception(message) | |
class TimeLimitExceeded(limiter: Limiter) | |
extends LimitExceeded(limiter, "Time limit exceeded (" + limiter.timeElapsed + ")") | |
class OpLimitExceeded(limiter: Limiter) | |
extends LimitExceeded(limiter, "Operation limit exceeded (" + limiter.opCount + ")") | |
class NoLimitExceeded(limiter: Limiter) | |
extends LimitExceeded(limiter, "No limit exceeded") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment