Why is Kotlin getting such an UndeclaredThrowableException and not a ParseException?

I have a method extension

that converts a string to Date in Kotlin.

fun String.convertToDate() : Date {
  var pattern: String = "dd-mm-yyyy"
  val dateFormatter = SimpleDateFormat(pattern)
  return dateFormatter.parse(this) // parse method throw ParseException
}

      

And this is the code where I am trying to catch a possible exception.

    try {
        "22---2017".convertToDate()
    } catch (ex: ParseException) {
        // ParseException supposed to be caught in this block

        logger.error("Parse exception occur")      
    } catch (ex: Exception) {

        // ParseException caught in this block

        logger.error("Exception occur")            
    }

      

Capture ParseException

in the last block where it is Exception

. But should it be caught in a ParseException block? What am I missing here?

=== UPDATE ===

I am developing a Spring MVC project. I ran the code in a simple standalone kotlin program where it behaves accordingly. But in my Spring project, it behaves differently. I give the complete code, Controller

and Service

.

controller

@PostMapping
@PreAuthorize("hasAnyRole('USER','ROLE_USER','ROLE_ADMIN','ADMIN')")
fun postAttendance(@RequestBody attendanceJson: AttendanceJsonWrapper,
                   request: HttpServletRequest): ResponseEntity<*> {
    val organization = getOrganizationFromSession(request)
    try {
        val attendanceBook: AttendanceBook = attendanceService.post(attendanceJson, organization.id!!)
        logger.info("Post successfully attendance book {}", attendanceBook)
    } catch (ex: SameDateAttendanceException) {
        logger.error("Duplicate attendance entry found at date [{}]", attendanceJson.date, attendanceJson.classId)
        return responseConflict(attendanceJson)
    } catch (ex: java.text.ParseException) {
        ex.printStackTrace()
        logger.error("Parse exception occur")
        return responseError(ErrorObject(
                attendanceJson,
                "date",
                Constants.INVALID_DATE_FORMAT,
                Constants.EXPECTED_DATE_FORMAT))
    } catch (ex: Exception) {
        ex.printStackTrace()
        logger.error("Exception occur")
        if (ex.cause is ParseException) {
            logger.info("What the hell is happening")
        }
    return responseOK(attendanceJson)
}

      

Service

@Service
open class AttendanceService constructor(val attendanceRepository: AttendanceRepository) {
@Transactional
open fun post(attendanceJsonWrapper: AttendanceJsonWrapper, orgId: Long): AttendanceBook {
    // ParseException should thrown from this line.
    val _attendanceDate = attendanceJsonWrapper.date.convertToDate()
    // Other logic goes here
    return attendanceRepository.save(attendanceBook)
}
}

      

Magazine

for a given input 28--2017

from the front end following the log.

2017-06-28 02:36:52.942 ERROR 4632 --- [nio-8080-exec-1] c.l.c.rest.AttendanceRestController      : Exception occur
2017-06-28 02:52:32.485  INFO 2796 --- [io-8080-exec-10] c.l.c.rest.AttendanceRestController      : What the hell is happening

      

Exception

java.lang.reflect.UndeclaredThrowableException
at com.lynas.service.AttendanceService$$EnhancerBySpringCGLIB$$7b42c004.post(<generated>)
at com.lynas.controller.rest.AttendanceRestController.postAttendance(AttendanceRestController.kt:34)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:124)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.text.ParseException: Unparseable date: "28--2017"
at java.text.DateFormat.parse(DateFormat.java:366)
at com.lynas.util.UtilKt.convertToDate(Util.kt:56)
at com.lynas.service.AttendanceService.post(AttendanceService.kt:23)
at com.lynas.service.AttendanceService$$FastClassBySpringCGLIB$$2b941a4b.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)
... 103 more

      

+3


source to share


2 answers


UndeclaredThrowableException

called by kotlin. What for? we know kotlin has no checked exceptions.

The UndeclaredThrowableException documentation says:

Bounced by a method call on a proxy instance if its call handler invocation method throws a thrown exception

On the other hand, Kotlin can throw any exceptions, but in java they will be: unchecked / checked exception and error. we know that almost all popular frameworks are based on a package java.reflect

that includes java.reflect.Proxy .

In short, when the kotlin function throws a java checked exception and does not declare an exception that can be thrown. then call java Proxy , you can get UndeclaredThrowableException like this .

In java, you can declare that the checked exception will be thrown like below:

//                v--- it is a checked exception in java
int read() throws IOException{/**/}

      

Thanks for @ glee8e for pointing out my mistake. you can also throw an exception in kotlin, since kotlin has no keyword throws

, so you must use @Throws

to declare that the exception will be thrown:

@Throws(IOException::class)
fun read():Int{/**/}

      


reproduce UndeclaredThrowableException in kotlin:



//throws a UndeclaredThrowableException takes the original IOException as its cause
// because java.lang.Runnable don't declare any checked exception at all
//                                                      |
//                                                      v
Runnable::class.proxying(::throwsAJavaCheckedException).run()


// throws the original IOException directly because java.util.concurrent.Callable
// has declared that it will be throwing a checked Exception
//                                                      |
//                                                      v
Callable::class.proxying(::throwsAJavaCheckedException).call()

      


fun throwsAJavaCheckedException(proxy:Any, method:Method, args:Array<Any>?): Any? {
    throw IOException();
}

typealias Invocation = (Any, Method, Array<Any>?) -> Any?;

fun <T:Any> KClass<T>.proxying(handler:Invocation) = cast(Proxy.newProxyInstance(
        ClassLoader.getSystemClassLoader(),
        arrayOf(java),
        handler
));

      

How can I avoid this problem?

If the function is written by itself, the solution is so much easier. yes, declare that the function will throw a checked exception. eg:

@Throws(ParseException::class)
fun convertToDate(){/**/}

      

OR write some gradle-plugin like allopen

i named it allthrows

here.

But you can also make some trade-offs. If you are not sure what will happen in frameworks like spring, you should move your call to a helper method. eg:

val task = Runnable::class.proxying(::throwsAJavaCheckedException)

//  v-- the result return by catch-block immediately if no exception occurs
val result : Unit = catch(task::run).by { actual: Throwable ->
    val exceptional: Unit = Unit;
    //    v--- you can choose return an exceptional value or rethrow the exception
    when (actual) {
        is RuntimeException -> exceptional
        is ParseException -> logger.info(acutal)
        else -> throw actual
    }
}


val result : Unit? = catch(task::run).only { actual:Throwable ->
// only handle the exception don't return the exceptional value
    logger.info(actual);
}

      


inline fun <T> catch(crossinline block: () -> T): () -> T {
    return { block(); };
}

inline fun <T> (() -> T).by(exceptionally: (Throwable) -> T): T {
    return only { exceptionally(it) }!!
}

inline fun <T : R, R> (() -> T).only(exceptionally: (Throwable) -> R): R? {
    try {
        return invoke();
    } catch(e: UndeclaredThrowableException) {
        return exceptionally(e.cause ?: e);
    } catch(e: Exception) {
        return exceptionally(e);
    }
}

      

+4


source


Your service seems to be running in a different calling context than your controller. Since the service is throwing an exception, you cannot catch it in the controller; it looks like you are calling the service directly, but because of the code injection, you really don't. So what happens is that the calling context (usually a thread) ends up with an exception. This translates to UndeclaredThrowableException

with the original exception as the reason.

There are two ways to deal with this:



  • catch the exception locally in the service where the exception is thrown;
  • catch UndeclaredThrowableException

    in a separate try / catch and then call the reason again.

The first option should be preferred, but you need to handle the exception in the service. The second one looks too much like a hack to me, but it doesn't require setting up exception handling in the service instead of the controller.

+1


source







All Articles