Raul Bertone is almost right, but not quite. The logic must be reversed and the ExceptionConversionAspect
must be the first in precedence.
Fully working sample for Java SE (I just emulate the Java EE exception):
Helper classes:
package javax.validation;
public class ConstraintViolationException extends RuntimeException {
private static final long serialVersionUID = -8041265519275356912L;
public ConstraintViolationException(String arg0) {
super(arg0);
}
}
package com.myexample.validator.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Validate {}
package com.myexample.service;
public class InvalidServiceInputException extends RuntimeException {
public InvalidServiceInputException(String arg0, Throwable arg1) {
super(arg0, arg1);
}
}
Sample driver application:
The driver application is annotated by @Validate
and emulates a service - see package name. It loops through 10 method calls, catching exceptions and printing them to standard output so as to show that they are indeed converted as desired.
package com.myexample.service;
import com.myexample.validator.annotation.Validate;
@Validate
public class Application {
public void doSomething(int i) {
System.out.printf("Doing something #%d%n", i);
}
public static void main(String[] args) {
Application application = new Application();
for (int i = 0; i < 10; i++) {
try {
application.doSomething(i + 1);
}
catch (Exception e) {
System.out.println(e);
System.out.println(" cause: " + e.getCause());
}
}
}
}
Aspects:
The validation aspect randomly throws a ConstraintViolationException
for demo purposes.
package com.myexample.aspect;
import java.util.Random;
import javax.validation.ConstraintViolationException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class ValidateAspect {
private static final Random RANDOM = new Random();
@Around("execution(public !static * *(..)) && @within(com.myexample.validator.annotation.Validate)")
public Object validateMethodInvocation(ProceedingJoinPoint thisJoinPoint) throws Throwable {
Object result = thisJoinPoint.proceed();
if (RANDOM.nextBoolean())
throw new ConstraintViolationException("uh-oh");
return result;
}
}
The exception conversion aspect has an additional @DeclarePrecedence
annotation now.
package com.myexample.aspect;
import javax.validation.ConstraintViolationException;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclarePrecedence;
import com.myexample.service.InvalidServiceInputException;
@Aspect
@DeclarePrecedence("ExceptionConversionAspect, *")
public class ExceptionConversionAspect {
@AfterThrowing(pointcut = "execution(* com.myexample.service..*(..))", throwing = "e")
public void convertServiceException(Exception e) {
if (e instanceof ConstraintViolationException) {
throw new InvalidServiceInputException("The service inputs failed validation", e);
}
}
}
Console output:
Doing something #1
Doing something #2
com.myexample.service.InvalidServiceInputException: The service inputs failed validation
cause: javax.validation.ConstraintViolationException: uh-oh
Doing something #3
com.myexample.service.InvalidServiceInputException: The service inputs failed validation
cause: javax.validation.ConstraintViolationException: uh-oh
Doing something #4
Doing something #5
Doing something #6
com.myexample.service.InvalidServiceInputException: The service inputs failed validation
cause: javax.validation.ConstraintViolationException: uh-oh
Doing something #7
Doing something #8
Doing something #9
Doing something #10