I cannot get my afterThrowing Spring AOP advice to fire,
I have made the point cut as generic as possible now and it still does not fire
I hope this is just a poor pointcut but I cannot see why, I would be grateful if anyone could see why
Advice
//Generic Exceptions
#AfterThrowing(value = "execution(* *(..)) throws Exception", throwing = "exception")
public void loggingGenericException(JoinPoint joinPoint, Exception exception) {
String classMethod = this.getClassMethod(joinPoint);
String stackTrace = "";
for (StackTraceElement element : exception.getStackTrace()) {
stackTrace += element.toString() + "\n";
}
String exceptionMessageAndStackTrace = exception.getMessage() + "\n" + stackTrace;
if (exception instanceof EmptyResultSetException) {
this.infoLevelLogging(joinPoint, classMethod);
} else {
this.errorLevelLogging(joinPoint, classMethod, exceptionMessageAndStackTrace);
}
}
Method that should be advised
public void getStudentTranscript(String studentId) throws RestClientException,IllegalArgumentException{
if (!this.serviceUrl.isEmpty()) {
if(studentId.isEmpty())
{
throw new IllegalArgumentException("studentId empty");
}
this.transcript = (Transcript) super.getForObject(this.serviceUrl,Transcript.class, studentId);
} else {
throw new IllegalArgumentException("url is empty");
}
}
If I run a test to check it is applied it is not working the test looks like this
#Test
public void testLoggingFiredOnExceptionInTranscriptRepository() throws Exception
{
Log log;
log = mock(Log.class);
when(log.isErrorEnabled()).thenReturn(true);
try {
loggingAspects.setLogger(log);
transcriptRepository.setServiceUrl("");
transcriptRepository.getStudentTranscript("12345");
} catch (RuntimeException e) {
System.out.println("e = " + e);
verify(log, times(1)).isErrorEnabled();
verify(log, times(1)).error(anyString());
}
}
The system out shows an exception fired
Can anyone offer any advice ( pun intended) :-)
Did you put the <aop:aspectj-autoproxy /> element in your spring configuration file? Otherwise, the AOP annotations won't be interpreted.
FYI, after having read your question, I created a sample project on my own and the #AfterThrowing annotation just works as it should.
Related
So I'm trying to retry for specific exceptions and created a bean which has shouldRetry(Throwable t) function. The function returns true if exception has to be retried, otherwise false.
But What I'm observing is shouldRetry(Throwable t) is executing twice(log is printing twice) for one retry attempt, however serviceImpl from where exception is being thrown is executing only once for one retry attempt.
Could someone please let me know if I'm doing something wrong here, or is it the default behavior/bug with spring retry itself.
#Component("dbRecoverableExceptionHandler")
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
#Slf4j
public class DBRecoverableExceptionHandler {
private final Environment environment;
private final MultiTaggedCounter exceptionRetryCounter;
public Boolean isRetryable(Throwable t) {
String[] recoverableExceptionClasses = environment
.getRequiredProperty("db-recoverable-exception-classes", String[].class);
for (String s1 : recoverableExceptionClasses) {
if (t.getClass().getSimpleName().contains(s1)) {
exceptionRetryCounter.increment(1, s1);
log.warn("Retrying for exception " + t.toString());
return true;
}
}
return false;
}
}
#Retryable(exceptionExpression = "#{#dbRecoverableExceptionHandler.isRetryable(#root)}",
maxAttemptsExpression = "#{${max-attempts}}",
backoff = #Backoff(delayExpression = "#{${retry-backoff-delay-time}}",
multiplierExpression = "#{${retry-backoff-multiplier}}"))
It is as expected.
The method will be called by the RetryTemplate twice for each execution...
while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
try {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Retry: count=" + context.getRetryCount());
}
// Reset the last exception, so if we are successful
// the close interceptors will not think we failed...
lastException = null;
return retryCallback.doWithRetry(context);
}
catch (Throwable e) {
lastException = e;
try {
registerThrowable(retryPolicy, state, context, e);
}
catch (Exception ex) {
throw new TerminatedRetryException("Could not register throwable",
ex);
}
finally {
doOnErrorInterceptors(retryCallback, context, e);
}
if (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
...
The first call to canRetry() (in the while loop) is skipped on the very first call since there is no exception yet, on subsequent iterations, when the method throws an exception, it is called twice.
method I am testing (the method setEventHubDataPayload throws JSONException and JsonProcessingException):
public class EventHubMapper {
//inits
public byte[] toEventDataJsonByteArray(UserRecord inbound) {
EventHubDto ehDto = new EventHubDto();
ehDto.setEventTypeVersion(inbound.getVersion());
ehDto.setEventId(inbound.getNotificationId());
JSONObject eventJson = new JSONObject(ehDto);
try {
eventJson.put("data", setEventHubDataPayload(ehDto, inbound));
} catch (JSONException e) {
analytics.trackError(AnalyticsConstants.EventHub.JSON_MAPPING_ERROR, e.toString());
} catch (JsonProcessingException e) {
analytics.trackError(AnalyticsConstants.EventHub.JSON_PROCESSING_ERROR, e.toString());
}
return eventJson.toString().getBytes();
}
}
unit test code:
#Test
public void toEventDataByteArray_JsonException() throws JSONException, JsonProcessingException {
EventHubMapper ehmMock = Mockito.spy(eventHubMapper);
doThrow(new JSONException("blah")).when(ehmMock).setEventHubDataPayload(any(), any());
eventHubMapper.toEventDataJsonByteArray(setUpMockUserRecord());
verify(analytics, times(1)).trackError( AnalyticsConstants.EventHub.JSON_MAPPING_ERROR, new JSONException("blah").toString());
}
I've tried using more specific matchers ... ex: any(EventHubDto.class) or any(UserRecord.class) and got the same result:
Wanted but not invoked:
analytics.trackError(
"EventHub_Publish_Error",
""
;
and also
Actually, there were zero interactions with this mock.
what is going on here?
I think you need to call like below while testing.
ehmMock.toEventDataJsonByteArray(setUpMockUserRecord());
Given the changes in the logging that were done in https://github.com/spring-projects/spring-security-oauth/issues/1271 and https://github.com/spring-projects/spring-security-oauth/issues/1290, I think it may be hard to please everyone with the logging that is present in the token endpoint. For instance, I would like to catch anything that falls to the simple #ExceptionHandler(Exception.class) to be an error statement with a log with a stack trace.
What would be the best way to intercept the exceptions that occur in the error endpoint, so that custom logging could be applied?
We can use override HandlerExceptionResolverComposite exception handler defined in WebMvcConfigurationSupport. This will composites all exception resolvers in into one exception resolver. We can then define your own exception resolvers.
One of the exception resolvers we can use is ExceptionHandlerExceptionResolver, this will enable AOP based exception handling by involving classes with #ControllerAdvice annotation.
In our custom controller advice, we can use handler for different exceptions:
#ExceptionHandler({OAuth2Exception.class})
public ResponseEntity<Object> handleOAuth2Exception(final OAuth2Exception exception, final WebRequest request) {
LOGGER.debug("OAuth failed on request processing", exception);
How we ended up solving this was using spring-aop. We simply intercepted the correct places and log an error message during it:
#Slf4j
#Aspect
#Component
public class OAuthErrorLoggingAspect {
private static final String ERROR_MESSAGE = "Error during token generation: ";
#Before("execution("
+ "public * "
+ "org.springframework.security.oauth2.provider.endpoint"
+ ".TokenEndpoint.handleException(Exception)) && args(ex))")
public void handleExceptionLogging(Exception ex) {
if (ex instanceof ClientAbortException) {
log.debug(ERROR_MESSAGE, ex);
} else {
log.error(ERROR_MESSAGE, ex);
}
}
#Before("execution("
+ "public * "
+ "org.springframework.security.oauth2.provider.endpoint"
+ ".TokenEndpoint.handleHttpRequestMethodNotSupportedException("
+ "org.springframework.web.HttpRequestMethodNotSupportedException)) && args(ex))")
public void handleHttpRequestMethodNotSupportedLogging(HttpRequestMethodNotSupportedException ex) {
log.debug(ERROR_MESSAGE, ex);
}
#Before("execution("
+ "public * "
+ "org.springframework.security.oauth2.provider.endpoint"
+ ".TokenEndpoint.handleClientRegistrationException("
+ "Exception)) && args(ex))")
public void handleClientRegistrationErrorLogging(Exception ex) {
log.debug(ERROR_MESSAGE, ex);
}
#Before("execution("
+ "public * "
+ "org.springframework.security.oauth2.provider.endpoint"
+ ".TokenEndpoint.handleException("
+ "org.springframework.security.oauth2.common.exceptions.OAuth2Exception)) && args(ex))")
public void handleOAuth2ExceptionLogging(OAuth2Exception ex) {
log.debug(ERROR_MESSAGE, ex);
}
}
I want to write junit test case for the below code with springJunitRunner.
the below piece of code is one service in a class.
#Component
#Path(/techStack)
public class TechStackResource {
#Autowired
private transient TechStackService techStackService;
#GET
#Path("/{id}")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response getTechStackById(final #PathParam("id") Integer technicalstackid) {
final TechStackResponse response = new TechStackResponse();
int statusCode = Constants.HTTP_STATUS_OK_200;
try {
TechStackModel techStackModel = techStackService.findObjectById(technicalstackid);
response.setGetTechStackDetails(GetTechStackDetails.newBuilder().technicalStack(techStackModel).build());
if (techStackModel == null) {
statusCode = Constants.HTTP_STATUS_ERROR_404;
}
} catch (EmptyResultDataAccessException erde) {
} catch (Exception e) {
LOGGER.error("Exception occured in TechStackResource.getTechStackById(technicalstackid) ", e);
throw new APMRestException(
"Exception while executing TechStackResource.getTechStackById(technicalstackid) ",
Constants.UNKNOW_ERROR, e);
}
return Response.status(statusCode).entity(response).build();
}
}
the configuration in web.xml for servlet is
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
Since you are using Jersey as well as Spring, you can use the SpringJunitRunner only to wire-up TechStackResource with its dependency TechStackService.
In order to test your REST handler method getTestStackById, you could go the POJO approach and invoke it directly. Alternatively, you can use Jersey's own MockWeb environment. To find out more about this, I recommend looking at the Jersey example sources, e.g. HelloWorld.
How does Spring know that ThrowsAdvice.afterThrowing needs to be called?
I found documentation on the class here but I was wondering if anyone has an elegant explanation of how exactly reflection is used to look for that "afterThrowing" method specificially. I just want to see the code that does that so I can understand it better.
A link to some source code would be a sufficient answer.
You're looking at very old documentation (though the current one doesn't say much more).
Spring uses a ThrowsAdviceInterceptor to handle ThrowsAdvice. You can find version 4.1.4.RELEASE source code here.
Its constructor
public ThrowsAdviceInterceptor(Object throwsAdvice) {
Assert.notNull(throwsAdvice, "Advice must not be null");
this.throwsAdvice = throwsAdvice;
Method[] methods = throwsAdvice.getClass().getMethods();
for (Method method : methods) {
if (method.getName().equals(AFTER_THROWING) &&
(method.getParameterTypes().length == 1 || method.getParameterTypes().length == 4) &&
Throwable.class.isAssignableFrom(method.getParameterTypes()[method.getParameterTypes().length - 1])
) {
// Have an exception handler
this.exceptionHandlerMap.put(method.getParameterTypes()[method.getParameterTypes().length - 1], method);
if (logger.isDebugEnabled()) {
logger.debug("Found exception handler method: " + method);
}
}
}
if (this.exceptionHandlerMap.isEmpty()) {
throw new IllegalArgumentException(
"At least one handler method must be found in class [" + throwsAdvice.getClass() + "]");
}
}
scans for appropriate methods and registers them. It then wraps the target method invocation
#Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable ex) {
Method handlerMethod = getExceptionHandler(ex);
if (handlerMethod != null) {
invokeHandlerMethod(mi, ex, handlerMethod);
}
throw ex;
}
}
and invokes the handler if an exception is thrown.