Is it possible to have two AfterThrows pieces of advice be applied to the same pointcut restricted by specific Exception type where one exception is a superclass of the other with, in the case of the subclass being captured, only one advice being executed?
I want to translate runtime exceptions (both custom and standard java ones) being chucked out of a service layer, where I do some specific translation in certain cases and then have a catch-all type piece of advice to translate anything truly unexpected:
#AfterThrowing(pointcut = "execution(* com.my.company.api.*(..))", throwing = "rnfex")
public void doTranslationAction(ResourceNotFoundException rnfex) {
// throw new WebApplicationException with Status.NOT_FOUND;
}
#AfterThrowing(pointcut = "execution(* com.my.company.api.*(..))", throwing = "aex")
public void doTranslationAction(AuthorisationException aex) {
// throw new WebApplicationException with Status.NOT_AUTHORISED;
}
#AfterThrowing(pointcut = "execution(* com.my.company.api.*(..))", throwing = "throwable")
public void doTranslationAction(Throwable throwable) {
// Log something here about this unexpected exception
// throw new WebApplicationException with Status.INTERNAL_SERVER_ERROR
}
I find that in this case, if I throw an exception which is explicitly catered for then the correct method is called, a translated exception is thrown, which is then captured by the broader 'Throwable' advice, then translated again into the catch-all INTERNAL_SERVER_ERROR WAE. This isn't unexpected, but just not quite what I was looking for.
To get around this I've got a single piece of advice which captures all Throwable types and then uses 'instanceof' to decide whether this is an expected custom runtime exception that I can translate into a specific WAE or not.
If I ever see 'instanceof' I'm sure I've done something to be ashamed of, but I'm not sure if there's a better way of solving this problemette without it?
I'm also against converting my expected custom exceptions to checked exceptions and then catching Runtime exception as the catch all, which could be one solution.
I wrote a similar aspect a while ago and I ended up using "instance of". I don't think there's a problem with that.
i am not pretty sure... but just wondering whether your last(third) advice can be written in the below fashion
execution( * com.my.company.api..*(..)) and !execution( * com.my.company.api.XyzAspect..*(..))
public void doTranslationAction(Throwable throwable) {
// Log something here about this unexpected exception
// throw new WebApplicationException with Status.INTERNAL_SERVER_ERROR
}
where XyzAspect.java is the #Aspect class where in you are writing these 3 advices.
Related
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. :-)
Here is my example bellow
public void test() throws Exception {
try {
int i = 1/0;
System.out.println(i);
} catch (Exception e) {
//the exception have been swallowed.
}
}
and the problem is spring aop's AfterThrowing can't work for this. if i remove the try-catch block.it works well then. how can i solve this problem. thanks for any suggestions.
Although it's unclear from question what you intend to achieve but this is the intended behavior of #AfterThrowing advice.
From docs -
After throwing advice runs when a matched method execution exits by throwing an exception.
which means that the advice will be executed only if an exception is thrown from method. In your sample the the exception is consumed and method exits normally thus no advice execution occurs.
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.
Some code like this:
public class A {
#Autoware
private B b;
public void a() {
//AAA: some logic process that maybe throw exception
b.b();
}
}
public class B {
public void b() {
//BBB: some logic process maybe also throw exception
}
}
Both exceptions in A.a() and B.b() need to be intercept, so i use #AfterThrowing annotation do it. but the question is, when i call A.a() in other code and exception has occurred in B.b(), the Advice will execute twice! because exception that occurred in B.b() was propagating to its caller A.a().
I can't swallow the exception silently, because i use spring-amqp, above codes is on Consumer side, i need some message processing that based on the exceptions that occurred in Consumer.
#Around does not work too since i can't swallow the throwed exception.
So, How can i intercept a exception just when it occurred? ignore propagation of it.
Any reply is greatly appreciated.
Running ex.getMessage() gives me:
Property 'firstname' threw exception; nested exception is javax.validation.ValidationException:
Error validating field firstname of class com.inferoquest.entity.Employee:
[ConstraintViolationImpl{interpolatedMessage='Name cannot be shorter than 2 characters',
propertyPath=firstname, rootBeanClass=class com.inferoquest.entity.Employee,
messageTemplate='Name cannot be shorter than 2 characters'}]
from which I'd like to extract Name cannot be shorter than 2 characters.
Update: Maybe I should add also that I'd like to do this in a clean manner, not by regex :-)
I've already seen this thread on the subject. Its answer might contain the solution to me but to be honest I thought it seemed overly complicated for such a simple task and honestly couldn't understand it well enough to use it.
Any ideas?
A ConstraintViolationException wraps a set of ConstraintViolations (see the JavaDoc for more details). You can obtain these violations by invoking getConstraintViolations() on the caught exception, iterate over the set and build a message with all messages of the contained violations.
I would like to add updated solution with Java 8 Steam API.
#ExceptionHandler(ConstraintViolationException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException exception) {
List<String> errorMessages = exception.getConstraintViolations()
.stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.toList());
return ResponseEntity.badRequest().body(errorMessages.toString());
}
Of course responding with only string is not a good idea, you can use your own custom Response class.
Implementation to the above solution.
#RestControllerAdvice(basePackageClasses = RepositoryRestExceptionHandler.class)
public class GlobalExceptionHandler {
#ExceptionHandler(ConstraintViolationException.class)
public ResponseObject handleConstaintViolatoinException(final ConstraintViolationException ex) {
StringBuilder message = new StringBuilder();
Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
for (ConstraintViolation<?> violation : violations) {
message.append(violation.getMessage().concat(";"));
}
return new ResponseObject(HttpStatus.PRECONDITION_FAILED.value(), message.toString());
}
}
The easiest way to get only the message from this line is to write in the catch section:
ex.getConstraintViolations().forEach(v -> System.out.println(v.getMessage()));
Search for this solution for about three hours and at least found this.
Of course, if you want to do something more complicated, you need something else. But this is enough for the beginning.