Hey guys. I'm hoping someone can help me achieve w...
# server
d
Hey guys. I'm hoping someone can help me achieve what I need to do here. My problem - I'm trying to use coroutines in my Spring `@RestController`s to do upstream REST API calls, using Retrofit 2.6.1. I'm getting an unexpected behavior though, when Retrofit throws a SocketTimeoutException. Here's some simplified sample code:
Copy code
@GetMapping
@Throws(RetrofitCallExecutionException::class)
fun myControllerEndpointMethod() {
    ...
    runBlocking(<http://Dispatchers.IO|Dispatchers.IO>) {
        val deferredUpstreamCall1 = async { upstreamApi.call1() }
        val deferredUpstreamCall2 = async { upstreamApi.call2() }
        val call1ResponseObject = getDeferredRetrofitResponseBody("upstreamApi.call1()", deferredUpstreamCall1, true)
        val call2ResponseObject = getDeferredRetrofitResponseBody("upstreamApi.call2()", deferredUpstreamCall2, true)
       ...
    }
}


open suspend fun <T> getDeferredRetrofitResponseBody(deferredResponseName: String, deferredResponse: Deferred<Response<T>>, throwOnError: Boolean = false) : T? {
	try {
		val response = deferredResponse.await()
		return if(response.isSuccessful) {
			val body = response.body()
			logger.debug("Got successful response for $deferredResponseName: $body")
			body
		} else{
			val errorMessage = "Error getting response for $deferredResponseName! Response Code[${response.code()}] Error Body[${getErrorResponseBody(response.errorBody())}]."
			logger.error(errorMessage)
			if(throwOnError) {
				throw RetrofitCallExecutionException(errorMessage)
			}
			null
		}
	} catch (ioe: IOException) {
		val errorMessage = "IOException while awaiting response for $deferredResponseName!"
		logger.error(errorMessage, ioe)
		if(throwOnError) {
			throw RetrofitCallExecutionException(errorMessage, ioe)
		}
	} catch (re: RuntimeException) {
		val errorMessage = "RuntimeException while awaiting response for $deferredResponseName!"
		logger.error(errorMessage, re)
		if(throwOnError) {
			throw RetrofitCallExecutionException(errorMessage, re)
		}
	}

	return null
}

@ExceptionHandler(RetrofitCallExecutionException::class)
fun handleRetrofitCallExecutionException(exception: RetrofitCallExecutionException) : ResponseEntity<PlatformErrorResponseDto> {
	<http://LOGGER.info|LOGGER.info>("handling RetrofitCallExecutionException raised. message[${exception.message}] cause[${exception.cause}]")
	return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build()
}
Now, lets say
upstreamApi.call1()
throws a SocketTimeoutException. If i step through the code in the debugger, I end up in my
catch (ioe: IOException)
block as expected. But I never end up in my
@ExceptionHandler
annotated method. I end up in my catch-all
@ExceptionHandler
that catches
Exception
, and the exception I'm getting is
UndeclaredThrowableException
, with the
SocketTimeoutException
as the cause and my
RetrofitCallExecutionException
in the list of suppressedExceptions. If I annotate my controller method with
@Throws(SocketTimeoutException::class)
and create an
@ExceptionHandler
method for SocketTimeoutException, I can catch it, and my
RetrofitCallExecutionException
is in the list of suppressedExceptions. So, how can I get Kotlin to throw my
RetrofitCallExecutionException
, so I can trap it in an
@ExceptionHandler
? I'm using Spring Boot 1.x, so I don't have any of the official support for Kotlin that Spring Boot 2.x brings. Could that be my problem?