Reputation: 21
I'm trying to develop a control structure that retries on some declared exceptions but throw others. The control structure works nicely but I have problem checking if the exception caught belongs to the types of declared exceptions. In more generic words, how do I check if a parameter is one of the declared list of type parameters?
Define a retry control structure:
def retry[T, R](times:Int=3, delay:Int=1000)(ex:Class[_<:Exception]*)(t:T)(block:T=>R):R = {
try {
block(t)
}
catch {
case e:Exception if (isOneOf(e, ex:_*) && times>0) => {
println(s"Exception $e happened, sleep $delay milliseconds")
Thread.sleep(delay)
println(s"$times times remain for retry before give up")
retry(times-1, delay)(ex:_*)(t)(block)
}
case e:Throwable => {
println(s"Exception $e is not handled")
throw e
}
}
}
define isOneOf function to check the declared and runtime exception type
def isOneOf[T:scala.reflect.ClassTag](obj:T, cs:Class[_]*) = {
val dc = obj.getClass
val rc = scala.reflect.classTag[T].runtimeClass
cs.exists(c=>c.isAssignableFrom(dc) || c.isAssignableFrom(rc))
}
define a function that throws multiple exceptions
def d(str:String) = {
val r = Math.random()
println(r)
if (r>0.6) throw new IllegalArgumentException
else if (r>0.4) throw new java.io.IOException
else if (r>0.2) throw new UnsupportedOperationException
else println(str)
}
and I can call the function with retry as:
retry(3, 1000)(classOf[IllegalArgumentException], classOf[java.io.IOException])("abc"){
x=> d(x)
}
I want to retry on IllegalArgumentException and IOException, but will throw UnsupportedOperationException.
My goal is to call the function like this:
retry(3, 1000)[IllegalArgumentException, java.io.IOException]("abc"){
x=> d(x)
}
For the retry structure, the list of declared exceptions is passed in dynamically at runtime. So multiple exception case statement does not work for me. When an exception is caught, I match it with generic exception, check the exception type with the isOneOf function. Ideally the function will take a sequence of types, instead of a sequence of classes. How do I pass in a sequence of exception types, rather than a sequence of classes and check the caught exception against the sequence of types?
Upvotes: 1
Views: 916
Reputation: 434
What do you think about replacing the list of exceptions with the function that knows which of exceptions are recoverable (and which you can build dynamically):
def retry[T, R](times: Int = 3, delay: Int = 1000)(t: T)(block: T => R)(recoverable: Throwable => Boolean): R = {
Try(block(t)) match {
case Success(value) => value
case Failure(throwable) => if (recoverable(throwable)) retry(times - 1, delay)(t)(block)(recoverable) else throw throwable
}
}
recoverable
function could look like this:
def recoverable(throwable: Throwable): Boolean = throwable match {
case exc @ (_: IllegalArgumentException | _: IOException) => true
case _ => false
}
Upvotes: 2