I am working on a Spring Boot application. I have defined an exception aspect, in order to customize and send meaningful error codes in the API response for service classes something like the below:
@Aspect
@Order(0)
public class TcInternalExceptionAspect {
private static final String INTERNAL_SERVER_ERROR = "Internal Server Error";
private final Logger log = LoggerFactory.getLogger(TcInternalExceptionAspect.class);
@Pointcut("@within(org.springframework.stereotype.Service)")
public void applicationServicePointcut() { }
@AfterThrowing(pointcut = "applicationServicePointcut()", throwing = "e")
public void translate(JoinPoint joinPoint, Throwable e) {
String resourceId = getResourceId();
if (e instanceof JDBCException) {
String message = (StringUtils.isBlank(e.getMessage()) ?
e.getClass() : e.getMessage());
log.error(message);
throw new BadServerStateException("Internal Server Error", resourceId,
DATABASE_ERROR_CODE);
} else {
String message = (StringUtils.isBlank(e.getMessage()) ?
e.getClass() : e.getMessage());
log.error(message);
throw new BadServerStateException("Internal Server Error", resourceId,
INTERNAL_ERROR_CODE);
}
}
BadServerStateException
looks like the below:
import org.zalando.problem.AbstractThrowableProblem;
public class BadServerStateException extends AbstractThrowableProblem {
private static final long serialVersionUID = 1L;
private final String entityName;
private final String errorKey;
private final String errorType;
public BadServerStateException(String defaultMessage, String entityName, String errorKey) {
this(defaultMessage, entityName, Status.INTERNAL_SERVER_ERROR, errorKey,
ErrorTypes.SYSTEM_ERROR.name());
}
public BadServerStateException(String defaultMessage, String entityName, String errorKey,
String errorType) {
this(defaultMessage, entityName, Status.INTERNAL_SERVER_ERROR, errorKey, errorType);
}
public BadServerStateException(String defaultMessage, String message, String entityName,
StatusType statusType, String errorKey, String errorType) {
super(null, defaultMessage, statusType, null, null, null,
getAlertParameters(message));
this.entityName = entityName;
this.errorKey = errorKey;
this.errorType = errorType;
}
public BadServerStateException(String defaultMessage, String entityName, StatusType statusType,
String errorKey, String errorType) {
super(null, defaultMessage, statusType, null, null, null,
getAlertParameters(defaultMessage));
this.entityName = entityName;
this.errorKey = errorKey;
this.errorType = errorType;
}
private static Map<String, Object> getAlertParameters(String defaultMessage) {
Map<String, Object> parameters = new HashMap<>();
if (!StringUtils.isEmpty(defaultMessage)) {
parameters.put(EXCEPTION_MESSAGE_PARAMETER_NAME, defaultMessage);
}
return parameters;
}
public String getEntityName() {
return entityName;
}
public String getErrorKey() {
return errorKey;
}
public String getErrorType() {
return errorType;
}
}
In case of any API error, I am getting an error log something like the below:
com.mycom.tc.exception.external.BadServerStateException: Internal Server Error
at com.mycom.tc.exception.TcInternalExceptionAspect.translate(TcInternalExceptionAspect.java:73)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:634)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:617)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:68)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
at com.mycom.tm.service.impl.MyServiceImpl$$EnhancerBySpringCGLIB$$b4c3687b.myFunction(<generated>)
at com.mycom.tm.web.rest.MyResource.createDocument(MyResource.java:46)
at com.mycom.tm.web.rest.MyResource$$FastClassBySpringCGLIB$$43df49a4.invoke(<generated>)
As you can see in the above log snipper, I am not getting the exact line of error in myFunction
, not able to understand how to get this.
Could someone please help here? Thanks.
CodePudding user response:
The effect you are seeing is not due to CGLIB, it is due to the way you handle exceptions. It is not even related to Spring AOP or Spring as such, really.
Does your BadServerStateException
not have a constructor with a Throwable cause
like this?
package de.scrum_master.spring.q73557968;
public class BadServerStateException extends RuntimeException {
public BadServerStateException(String internalServerError, String resourceId, Object p2) {
this(internalServerError, resourceId, p2, null);
}
public BadServerStateException(String internalServerError, String resourceId, Object p2, Throwable cause) {
super(internalServerError ": " resourceId " -> " p2, cause);
}
}
Then, given you have an application and a service like this, ...
package de.scrum_master.spring.q73557968;
import org.hibernate.JDBCException;
import org.springframework.stereotype.Service;
import java.sql.SQLException;
@Service
public class MyService {
public void doSomething() {
System.out.println("My service");
}
public void doSomethingElse() {
throw new JDBCException("uh-oh, database error", new SQLException("syntax error in SQL"));
}
public void doAnotherThing() {
throw new ClassCastException("oops, cannot cast Foo to Bar");
}
}
package de.scrum_master.spring.q73557968;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
@SpringBootApplication
@Configuration
public class DemoApplication {
public static void main(String[] args) throws Throwable {
try (ConfigurableApplicationContext appContext = SpringApplication.run(DemoApplication.class, args)) {
doStuff(appContext);
}
}
private static void doStuff(ConfigurableApplicationContext appContext) {
MyService myService = appContext.getBean(MyService.class);
myService.doSomething();
try {
myService.doSomethingElse();
}
catch (Exception e) {
e.printStackTrace();
}
try {
myService.doAnotherThing();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
..., just change the aspect to use the constructor with the Throwable
parameter and pass on the original exception:
package de.scrum_master.spring.q73557968;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.hibernate.JDBCException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Order(0)
public class TcInternalExceptionAspect {
private static final String INTERNAL_SERVER_ERROR = "Internal Server Error";
private static final Object DATABASE_ERROR_CODE = 11;
private static final Object INTERNAL_ERROR_CODE = 22;
private final Logger log = LoggerFactory.getLogger(TcInternalExceptionAspect.class);
@Pointcut("@within(org.springframework.stereotype.Service)")
public void applicationServicePointcut() {}
@AfterThrowing(pointcut = "applicationServicePointcut()", throwing = "e")
public void translate(JoinPoint joinPoint, Throwable e) {
String resourceId = getResourceId();
if (e instanceof JDBCException) {
String message = e.getMessage() == null || e.getMessage().trim().equals("")
? e.getClass().toString()
: e.getMessage();
log.error(message);
throw new BadServerStateException(INTERNAL_SERVER_ERROR, resourceId, DATABASE_ERROR_CODE, e);
}
else {
String message = e.getMessage() == null || e.getMessage().trim().equals("")
? e.getClass().toString()
: e.getMessage();
log.error(message);
throw new BadServerStateException(INTERNAL_SERVER_ERROR, resourceId, INTERNAL_ERROR_CODE, e);
}
}
private String getResourceId() {
return "ABC-123";
}
}
Then, on the console you should see:
2022-12-10 08:51:26.017 INFO 23880 --- [ main] d.s.spring.q73557968.DemoApplication : Started DemoApplication in 2.585 seconds (JVM running for 3.175)
My service
2022-12-10 08:51:26.031 ERROR 23880 --- [ main] d.s.s.q.TcInternalExceptionAspect : uh-oh, database error
2022-12-10 08:51:26.033 ERROR 23880 --- [ main] d.s.s.q.TcInternalExceptionAspect : oops, cannot cast Foo to Bar
de.scrum_master.spring.q73557968.BadServerStateException: Internal Server Error: ABC-123 -> 11
at de.scrum_master.spring.q73557968.TcInternalExceptionAspect.translate(TcInternalExceptionAspect.java:33)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:634)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:617)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:68)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698)
at de.scrum_master.spring.q73557968.MyService$$EnhancerBySpringCGLIB$$22cb0578.doSomethingElse(<generated>)
at de.scrum_master.spring.q73557968.DemoApplication.doStuff(DemoApplication.java:24)
at de.scrum_master.spring.q73557968.DemoApplication.main(DemoApplication.java:16)
Caused by: org.hibernate.JDBCException: uh-oh, database error
at de.scrum_master.spring.q73557968.MyService.doSomethingElse(MyService.java:14)
at de.scrum_master.spring.q73557968.MyService$$FastClassBySpringCGLIB$$7d395b25.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:783)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:64)
... 9 more
Caused by: java.sql.SQLException: syntax error in SQL
... 16 more
de.scrum_master.spring.q73557968.BadServerStateException: Internal Server Error: ABC-123 -> 22
at de.scrum_master.spring.q73557968.TcInternalExceptionAspect.translate(TcInternalExceptionAspect.java:40)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:634)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:617)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:68)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698)
at de.scrum_master.spring.q73557968.MyService$$EnhancerBySpringCGLIB$$22cb0578.doAnotherThing(<generated>)
at de.scrum_master.spring.q73557968.DemoApplication.doStuff(DemoApplication.java:30)
at de.scrum_master.spring.q73557968.DemoApplication.main(DemoApplication.java:16)
Caused by: java.lang.ClassCastException: oops, cannot cast Foo to Bar
at de.scrum_master.spring.q73557968.MyService.doAnotherThing(MyService.java:17)
at de.scrum_master.spring.q73557968.MyService$$FastClassBySpringCGLIB$$7d395b25.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:783)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:64)
... 9 more
See? Of course, you see the newly created exception and its current stack trace, but the causing exception's stack trace is also displayed. This is a standard means of exception handling in Java.