Why Kotlin receives such an UndeclaredThrowableException rather than a ParseException?

seal :

I have an extension method that converts 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 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")            
    }

The ParseException caught in the last block that is where Exception is caught. But should it be caught in the ParseException block ? What am I missing here ?

===UPDATE===

I am developing an Spring MVC project. I have run the code in simple stand alone kotlin program where it behaves accordingly. But in my spring project it behaves differently. I am giving the the full code, Controller and the Service layer.

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)
}
}

Log

for given input 28--2017 from front end following log produced.

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
holi-java :

The UndeclaredThrowableException is caused by kotlin. why? we know kotlin does not have checked exceptions.

The documentation of UndeclaredThrowableException says:

Thrown by a method invocation on a proxy instance if its invocation handler's invoke method throws a checked exception

On the other hand, Kotlin can throw any exceptions but in java them will are: unchecked/checked exception and error. we know almost all of the popular frameworks is created base on java.reflect package includes java.reflect.Proxy.

In short, when the kotlin function throw a java checked exception and don't declare the exception that it will maybe throwing. then call a java Proxy you maybe receive such a UndeclaredThrowableException.

In java you can declare a checked-exception will be throwing as below:

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

Thanks for @glee8e to points my mistake. you also can throws an exception in kotlin, since kotlin don't have throws keyword so you must using @Throws to declare an exception will be throwing:

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

let's reproduce the 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 to avoiding this problem?

If the function is wrote by yourself the solution is so simpler. yes, declare the function will be throwing a checked exception. for example:

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

OR write some gradle-plugin like as allopen, I named it allthrows here.

But you also can make some compromises. If you are not sure what will be happens in the frameworks like as spring, you should wrap your invocation into a helper method. for example:

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);
    }
}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=432464&siteId=1