package rocks.inspectit.server.spring.aop;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.stereotype.Component;
import rocks.inspectit.shared.all.exception.BusinessException;
import rocks.inspectit.shared.all.exception.RemoteException;
import rocks.inspectit.shared.all.spring.logger.Log;
/**
* Aspect that defines the around advice that is bounded to all methods of all classes in the
* rocks.inspectit.server.service package. The advice prints the exception if one is raised and
* provides additional information like what method was executed, what parameters where passed, etc.
* In addition all exceptions except our BusinessException are transformed to a
* {@link RemoteException}.
*
* @author Ivan Senic
*
*/
@Aspect
@Component
public class ExceptionInterceptor {
/**
* Logger for logging purposes.
*/
@Log
Logger log;
/**
* Field that identifies the cause field in a {@link Throwable} used for modifying it via
* reflection.
*/
private static Field throwableCauseField;
// get the #throwableCauseField via reflection
static {
try {
throwableCauseField = Throwable.class.getDeclaredField("cause");
throwableCauseField.setAccessible(true);
} catch (Exception e) {
throw new BeanInitializationException("Count not initialize " + ExceptionInterceptor.class.getName() + ". Check nested Exception.", e);
}
}
/**
* The advice.
*
* @param jp
* {@link ProceedingJoinPoint} holding all information about pointcut.
* @return Result of method invocation.
* @throws Exception
* If exception occurs we re-throw the {@link Exception} of some kind
*/
@Around("execution(* rocks.inspectit.server.service.*.*(..))")
public Object logServiceException(ProceedingJoinPoint jp) throws Exception {
try {
return jp.proceed();
} catch (BusinessException e) {
if (log.isDebugEnabled()) {
log.debug("BusinessException thrown in the service method " + jp.getSignature() + " executed with following parameters: " + Arrays.toString(jp.getArgs()) + ".", e);
}
// if it's our BusinessException set service signature and just re-throw it
e.setServiceMethodSignature(jp.getSignature().toString());
throw e;
} catch (RemoteException e) {
log.warn("Exception thrown in the service method " + jp.getSignature() + " executed with following parameters: " + Arrays.toString(jp.getArgs()) + ". Original exception class is: "
+ e.getOriginalExceptionClass(), e);
// if we already have remote one service signature and just re-throw it
e.setServiceMethodSignature(jp.getSignature().toString());
throw e;
} catch (Throwable t) { // NOPMD
log.warn("Exception thrown in the service method " + jp.getSignature() + " executed with following parameters: " + Arrays.toString(jp.getArgs()) + ".", t);
RemoteException transformException = transformException(t, new ArrayList<Throwable>());
transformException.setServiceMethodSignature(jp.getSignature().toString());
throw transformException;
}
}
/**
* Transforms the given throwable to the {@link RemoteException}.
*
* @param throwable
* {@link Throwable} to transform.
* @return {@link RemoteException}
*/
public static RemoteException transformException(Throwable throwable) {
try {
return transformException(throwable, new ArrayList<Throwable>());
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to transform exception to the RemoteException. Check nested Exception.", e);
}
}
/**
* Transforms the given throwable to the {@link RemoteException}.
*
* @param throwable
* {@link Throwable} to transform.
* @param visitedException
* List of already processed exceptions.
* @return {@link RemoteException}
* @throws IllegalAccessException
* If set can not be executed on the
* {@link ExceptionInterceptor#throwableCauseField}.
*/
private static RemoteException transformException(Throwable throwable, List<Throwable> visitedException) throws IllegalAccessException {
if ((throwable == null) || !visitedException.add(throwable) || (throwable instanceof RemoteException)) {
return null;
}
RemoteException transformedException = new RemoteException(throwable);
throwableCauseField.set(transformedException, transformException(throwable.getCause(), visitedException));
return transformedException;
}
}