What is the most likely cause of exceptions mysteriously escaping a try-catch block in this case? - spring

I am using a Spring WebClient in a Kotlin project like this:
data class DTO(val name: String)
#Component
class Runner: ApplicationRunner
{
override fun run(args: ApplicationArguments?)
{
try
{
val dto = get<DTO>()
}
catch (e: Exception)
{
println("ERROR, all exceptions should have been caught in 'get' ")
}
}
}
inline private fun<reified TResult: Any> get(): TResult?
{
var result: TResult? = null
try
{
result = WebClient.create("https://maps.googleapis.com/maps/api/nonexisting")
.get()
.retrieve()
.bodyToMono<TResult>()
.block()
}
catch (e: Exception)
{
println("WORKS AS EXPECTED!!")
}
return result
}
The client will throw an exception, because the API will return a 404. However the exception is not caught where it should be, namely in the body of the get function, but it is propagated to the outer exception handler.
It is interesting to note that this happens only if the exception is thrown by the WebClient. If I replace the code in the try clause with a simple throw Exception("error"), the exception is caught where it should be.
Similarly, when I change the signature of get to a non-generic inline private fun get(): DTO? the problem also goes away.
For an exception to escape the try-catch block seems like a fundamental bug in the Kotlin tools. On the other hand, the fact that this happens only with the WebClient class indicates that this is a Spring problem. Or, it may be just me, using the tools in a wrong way.
I am really baffled here and have no idea how to proceed. Any ideas on why this might be happening are most welcome. Just for completeness, this is what it looks like in the debugger:
EDIT
The issue goes away after upgrading Spring Boot to 2.0.0.M6, it is still present in M5.
So it seems that this was a Spring issue and not a Kotlin issue. On the other hand it would be still nice to understand how a library that you include can seemingly cause the program to violate the laws of the programming language it is written in.

I tried the code with Spring Boot version 2.0.0.M5 and 2.0.0.M6, and it seems the behavior of the following block is different between those 2 versions:
result = WebClient.create("https://maps.googleapis.com/maps/api/nonexisting")
.get()
.retrieve()
.bodyToMono<TResult>()
.block()
somewhere along the chain, on Spring Boot 2.0.0.M5, the WebClientResponseException is returned, on Spring Boot 2.0.0.M6 it is thrown.
If you add a e.printStackTrace() to your outer catch, you will notice that the stack trace is:
java.lang.ClassCastException:
org.springframework.web.reactive.function.client.WebClientResponseException
cannot be cast to com.example.demo.DTO at
com.example.demo.Runner.run(Test.kt:18) at
org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:780)
at
org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:770)
at
org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:760)
at
org.springframework.boot.SpringApplication.run(SpringApplication.java:328)
at
org.springframework.boot.SpringApplication.run(SpringApplication.java:1245)
at
org.springframework.boot.SpringApplication.run(SpringApplication.java:1233)
at com.example.demo.DemoApplicationKt.main(DemoApplication.kt:10)
So, actually, problem is, the returned WebClientResponseException is tried to be cast to DTO class on the moment of return of the call val dto = get<DTO>(). This means that, when you assign result = ..., there is no type checking done yet. So, if you change your code to, for example, call get<Object>() instead of get<DTO>(), it won't hit any catch blocks.
If you convert it to bytecode in IntelliJ Idea, and then decompile it to Java, you can see this block:
public class Runner implements ApplicationRunner {
public void run(#Nullable ApplicationArguments args) {
try {
Object result$iv = null;
try {
ResponseSpec $receiver$iv$iv = WebClient.create("https://maps.googleapis.com/maps/api/nonexisting").get().retrieve();
Mono var10000 = $receiver$iv$iv.bodyToMono((ParameterizedTypeReference)(new Runner$run$$inlined$get$1()));
Intrinsics.checkExpressionValueIsNotNull(var10000, "bodyToMono(object : Para…zedTypeReference<T>() {})");
result$iv = var10000.block();
} catch (Exception var7) {
String var5 = "WORKS AS EXPECTED!!";
System.out.println(var5);
}
DTO var2 = (DTO)result$iv;
} catch (Exception var8) {
String var3 = "ERROR, all exceptions should have been caught in 'get' ";
System.out.println(var3);
}
}
}
Here you can notice that casting to DTO is done on the point of method return (which is not a return anymore because it is inlined), after the inner catch block: DTO var2 = (DTO)result$iv;. It seems like that's the behavior for the inlined methods with reified type parameters.

This is due to SPR-16025 (see related commit) since the Kotlin extension is using internally the ParameterizedTypeReference variant, which has been fixed in Spring Framework 5.0.1, and transitively in Spring Boot 2.0.0.M6.
Note than if you use bodyToMono(TResult::class.java) with Spring Boot 2.0.0.M5, it will works as expected.

Related

JOOQ execution listener does not catch exception

I'm trying to implement a generic solution for optimized locking. What I want to achieve is to have a specific piece of code run when record's version changes. I have it implemented as an ExecuteListener instance that looks for DataChangedException. It's registered as a Spring bean.
class LockingListener : DefaultExecuteListener() {
override fun exception(ctx: ExecuteContext) {
val exception = ctx.exception()
if (exception is DataChangedException) {
ctx.exception(IllegalStateException("Accessed data has been altered mid-operation."))
}
}
}
#Configuration
class JooqConfig {
#Bean
fun lockingListenerProvider() = DefaultExecuteListenerProvider(LockingListener())
}
I had a breakpoint set in org.jooq.impl.ExecuteListeners#get and it does look like it gets picked up alongside LoggerListener and JooqExceptionTranslator.
When I try to run a test case though, DataChangedException does not get picked up on UpdateableRecord#update and I get the following stacktrace instead, no IllegalStateException in sight.
org.jooq.exception.DataChangedException: Database record has been changed or doesn't exist any longer
at org.jooq.impl.UpdatableRecordImpl.checkIfChanged(UpdatableRecordImpl.java:540)
at org.jooq.impl.UpdatableRecordImpl.storeMergeOrUpdate0(UpdatableRecordImpl.java:349)
at org.jooq.impl.UpdatableRecordImpl.storeUpdate0(UpdatableRecordImpl.java:241)
at org.jooq.impl.UpdatableRecordImpl.access$100(UpdatableRecordImpl.java:89)
at org.jooq.impl.UpdatableRecordImpl$2.operate(UpdatableRecordImpl.java:232)
at org.jooq.impl.RecordDelegate.operate(RecordDelegate.java:149)
at org.jooq.impl.UpdatableRecordImpl.storeUpdate(UpdatableRecordImpl.java:228)
at org.jooq.impl.UpdatableRecordImpl.update(UpdatableRecordImpl.java:165)
Debugging shows that LockingListener#exception does not even get entered into.
That exception is not part of the ExecuteListener lifecycle, i.e. the lifecycle that deals with interactions with the JDBC API. In other words, it's not a SQLException, it happens higher up the stack. Use the RecordListener.exception() callback, instead.

Spring Transactional - Ensure predictable behavior in tests and prod

I want to test the registration method of my UserService, which looks something like the below.
#Transactional
override fun register(userRegistration: UserRegistration): AuthDto {
val user = userRegistration.toUserEntity()
return try {
val entity = userRepository.save(user)
//entityManager.flush()
val id = entity.getIdOrThrow().toString()
val jwt = jwtService.createJwt(id)
entity.toAuthDto(jwt)
} catch (ex: PersistenceException) {
throw UserRegistrationException(userRegistration.username, ex)
}
}
Since there is a unique index on the userName of the User entity, I would like to assert that an exception is thrown when an already existing userName is registered. In this case I try to catch whatever exception is thrown and rethrow my own.
Now my test simply takes an existing userName and calls register.
#Test fun `register twice - should throw`() {
val existingRegistration = UserRegistration(testUserAdminName, "some", "test")
assertThrows<UserRegistrationException> {
userService.register(existingRegistration)
//entityManager.flush()
}
}
However, no exception is ever thrown, unless I explicitly flush via the entity manager. But then how can I throw my own exception?
Should I use flush in my UserService?
The answer came from M. Deinum.
Flushing is done on commit and that is also where the exception is being thrown. So if you want to directly get an exception you will have to call saveAndFlush instead of save (assuming you are using the JpaRepository as a base for your own repository)
I switched to JpaRepository and am now using saveAndFlush.

#Around Aspect in Spring AOP throwing Null Pointer Exception

Can someone help me out figuring why my #Around advice throws a Null Pointer Exception here. I have made sure that my advice returns an Object same as the method to proceed, yet I am getting the exception.
#Aspect
public class BasicAuthAspect
{
#Around("execution(* *..impl.PreferenceImpl.*(..))")
public Object auth(ProceedingJoinPoint joinPoint) {
log.debug("Inside Basic Auth check method..");
ServletRequestAttributes requestAttributes=null;
requestAttributes=(ServletRequestAttributes)
RequestContextHolder.getRequestAttributes();
if(requestAttributes!=null){
request =requestAttributes.getRequest();
Object[] signatureArgs = joinPoint.getArgs();
try {
return (Response) joinPoint.proceed(signatureArgs);
} catch (Throwable e) {
return Response.status(HttpStatus.SC_UNAUTHORIZED).build();
}
}
}
else{
return Response.status(HttpStatus.SC_UNAUTHORIZED).build();
}
}
}
Here is my method to proceed(PreferenceImpl.java):
#Override
public Response postPreferences( String preference) {
String responseReturned=searchDAO.postPreference( XSSUtils.normalize(preference));
if(responseReturned!=null)
response=Response.status("success").build();
else
response=Response.status("failure").build();
return response;
}
Here goes the error log:
java.lang.NullPointerException
at java.lang.String.<init>(String.java:556)[:1.7.0_80]
at org.apache.cxf.transport.http.AbstractHTTPDestination.getAuthorizationPolicyFromMessage(AbstractHTTPDestination.java:163)[173:org.apache.cxf.cxf-rt-transports-http:2.7.3]
at org.apache.cxf.transport.http.AbstractHTTPDestination.setupMessage(AbstractHTTPDestination.java:349)[173:org.apache.cxf.cxf-rt-transports-http:2.7.3]
at org.apache.cxf.transport.http.AbstractHTTPDestination.setupMessage(AbstractHTTPDestination.java:258)[173:org.apache.cxf.cxf-rt-transports-http:2.7.3]
at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.serviceRequest(JettyHTTPDestination.java:345)[179:org.apache.cxf.cxf-rt-transports-http-jetty:2.7.3]
at org.apache.cxf.transport.http_jetty.JettyHTTPDestination.doService(JettyHTTPDestination.java:319)[179:org.apache.cxf.cxf-rt-transports-http-jetty:2.7.3]
at org.apache.cxf.transport.http_jetty.JettyHTTPHandler.handle(JettyHTTPHandler.java:72)[179:org.apache.cxf.cxf-rt-transports-http-jetty:2.7.3]
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1040)[79:org.eclipse.jetty.server:7.6.8.v20121106]
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:976)[79:org.eclipse.jetty.server:7.6.8.v20121106]
You could have several problems here:
Your pointcut intercepts all PreferenceImpl methods, but it seems you only want to catch a single one. I suggest you make the pointcut more specific.
You are assuming return type Response in the advice method, but your pointcut assumes * and the advice method itself returns Object, so theoretically another method could return another type and the cast would fail (see first problem).
You are trying to provide arguments to proceed(), but that is actually not necessary. So just remove the getArgs() stuff, you do not need it.
If despite my hints you still have problems, let me know and I can provide some sample code. BTW, maybe you want to read a basic Spring AOP tutorial. :-)

Spring Boot Exception details

I am trying to log the exception in the spring boot based web service.
So I have used GlobalExceptionHandler
My code :
#ControllerAdvice
#RestController
public class GlobalExceptionHandler {
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = Exception.class)
public String handleException(Exception e){
System.out.println("Ankit == "+e.getMessage());
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
System.out.println(errors.toString());
return e.getMessage();
}
}
the code is working fine. What I want is exception details. I mean the code where the exception occurs? File name / line ? or do I have to parse the stacktrace? I mean spring boot must have thought something for this?
Using IDE
If you are using any IDE then go to Console Window.
Clear console
Repeat action that causes Exception
Search in Console (CTRL + F) for ERROR
Look for line above(Look for 2-3 lines if you don't find immediate above) the line which contains ERROR. This line has details of Class, Method where Exception has occurred.
Without looking at Console or Logs
If you want to use it in production then, handling atleast known exceptions(like BAD_REQUEST, NOT_FOUND etc.) the way it is done below might be helpful (adding an extra parameter to Exception Class) :
Employee employee = employeeService.getEmployeeById(employeeId);
if (null == employee) {
logger.error("No tenant exists for employeeId:"+employeeId);
throw new ObjectNotFoundException("Emplyee Not Found", this.getClass().getSimpleName();));
}
here this.getClass().getSimpleName(); will be passed as parameter from EmployeeController class. So in ObjectNotFoundException we can add a parameter ClassName and When you handle it in GlobalExceptionHandler, you can do it as it is done below,
#ControllerAdvice
#RestController
public class GlobalExceptionHandler {
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = Exception.class)
public String handleException(Exception e){
System.out.println("Ankit == "+e.getMessage());
StringWriter errors = new StringWriter();
e.printStackTrace(new PrintWriter(errors));
String classWithExceptionName = e.getClassName();
// you need to add this above getter method to your Exception Class
System.out.println(errors.toString());
return e.getMessage();
}
}
This is for known common exceptions. We need to add extra parameter(ClassName) to All Custom Exceptions that you are throwing and that might be little extra code but i think that is the way. Hope it helps now.

Handling exceptions in Spring MVC along with Rest API

I am using #ControllerAdvice annotation for defining exceptions at application level. Now the problem is I am having two #ControllerAdvice classes, one for REST and one for the normal web app. When I define #ExceptionHandler for Exception.class in both, only the first one is considered. How do I separate both? Or how can I catch an Exception and determine from where it has occured? Is there a way or else do I need to use controller-specific exception handlers?
I resolved this issue by creating a custom exceptions for my application and giving one exception handler method for each of them with #exception handler.
I also used aspects to make sure that every exception is converted to any of the custom exceptions.
#Aspect
#Component
public class ExceptionInterceptor {
#AfterThrowing(pointcut = "within(x.y.package..*)", throwing = "t")
public void toRuntimeException(Throwable t)
throws ApplicationException1, ApplicationException2,ApplicationException3 {
if (t instanceof ApplicationException1) {
throw (ApplicationException1) t;
} else if (t instanceof ApplicationException2) {
throw (ApplicationException2) t;
} else
throw (ApplicationException3) t;
}
}
These will transfer control to #controlleradvice.
I noticed this have been left for a month or so, so it might be old. But this article may help http://www.baeldung.com/2013/01/31/exception-handling-for-rest-with-spring-3-2/.
The section 3.5 is probably what you are looking for, a custom Exception Resolver.

Resources