Skip to content

Commit

Permalink
Merge pull request #873 from csokol/catching-interceptors-validation-…
Browse files Browse the repository at this point in the history
…errors

Handling ValidationExceptions in interceptors
  • Loading branch information
csokol committed Dec 29, 2014
2 parents 2f884e2 + 9503b57 commit 3268b17
Show file tree
Hide file tree
Showing 11 changed files with 292 additions and 99 deletions.
Expand Up @@ -28,6 +28,7 @@
import br.com.caelum.vraptor.interceptor.StepInvoker;
import br.com.caelum.vraptor.ioc.Container;

import br.com.caelum.vraptor.observer.ExecuteMethodExceptionHandler;
import com.google.common.base.Supplier;

/**
Expand All @@ -44,25 +45,28 @@ public class DefaultInterceptorHandlerFactory implements InterceptorHandlerFacto
private final InterceptorAcceptsExecutor acceptsExecutor;
private final CustomAcceptsExecutor customAcceptsExecutor;
private final InterceptorExecutor interceptorExecutor;
private final ExecuteMethodExceptionHandler executeMethodExceptionHandler;

/**
* @deprecated CDI eyes only
*/
protected DefaultInterceptorHandlerFactory() {
this(null, null, null, null, null, null);
this(null, null, null, null, null, null, null);
}

@Inject
public DefaultInterceptorHandlerFactory(Container container, StepInvoker stepInvoker,
CacheStore<Class<?>, InterceptorHandler> cachedHandlers, InterceptorAcceptsExecutor acceptsExecutor,
CustomAcceptsExecutor customAcceptsExecutor, InterceptorExecutor interceptorExecutor) {
CustomAcceptsExecutor customAcceptsExecutor, InterceptorExecutor interceptorExecutor,
ExecuteMethodExceptionHandler executeMethodExceptionHandler) {

this.container = container;
this.stepInvoker = stepInvoker;
this.cachedHandlers = cachedHandlers;
this.acceptsExecutor = acceptsExecutor;
this.customAcceptsExecutor = customAcceptsExecutor;
this.interceptorExecutor = interceptorExecutor;
this.executeMethodExceptionHandler = executeMethodExceptionHandler;
}

@Override
Expand All @@ -74,7 +78,7 @@ public InterceptorHandler get() {
return new AspectStyleInterceptorHandler(type, stepInvoker, container, customAcceptsExecutor,
acceptsExecutor, interceptorExecutor);
}
return new ToInstantiateInterceptorHandler(container, type);
return new ToInstantiateInterceptorHandler(container, type, executeMethodExceptionHandler);
}
});
}
Expand Down
Expand Up @@ -17,15 +17,16 @@

package br.com.caelum.vraptor.core;

import javax.enterprise.inject.Vetoed;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import br.com.caelum.vraptor.InterceptionException;
import br.com.caelum.vraptor.controller.ControllerMethod;
import br.com.caelum.vraptor.interceptor.Interceptor;
import br.com.caelum.vraptor.ioc.Container;
import br.com.caelum.vraptor.observer.ExecuteMethodExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.enterprise.inject.Vetoed;
import java.util.concurrent.Callable;

/**
* Instantiates the interceptor on the fly and executes its method.
Expand All @@ -39,28 +40,43 @@ public class ToInstantiateInterceptorHandler implements InterceptorHandler {

private final Container container;
private final Class<?> type;
private final ExecuteMethodExceptionHandler executeMethodExceptionHandler;

public ToInstantiateInterceptorHandler(Container container, Class<?> type) {
public ToInstantiateInterceptorHandler(Container container, Class<?> type, ExecuteMethodExceptionHandler executeMethodExceptionHandler) {
this.container = container;
this.type = type;
this.executeMethodExceptionHandler = executeMethodExceptionHandler;
}

@Override
public void execute(InterceptorStack stack, ControllerMethod method, Object controllerInstance)
public void execute(final InterceptorStack stack, final ControllerMethod method, final Object controllerInstance)
throws InterceptionException {
Interceptor interceptor = (Interceptor) container.instanceFor(type);
final Interceptor interceptor = (Interceptor) container.instanceFor(type);
if (interceptor == null) {
throw new InterceptionException("Unable to instantiate interceptor for " + type.getName()
+ ": the container returned null.");
}
if (interceptor.accepts(method)) {
logger.debug("Invoking interceptor {}", interceptor.getClass().getSimpleName());
interceptor.intercept(stack, method, controllerInstance);
executeSafely(stack, method, controllerInstance, interceptor);
} else {
stack.next(method, controllerInstance);
}
}

private void executeSafely(final InterceptorStack stack, final ControllerMethod method, final Object controllerInstance, final Interceptor interceptor) {
Try result = Try.run(new Callable<Void>() {
@Override
public Void call() throws Exception {
interceptor.intercept(stack, method, controllerInstance);
return null;
}
});
if (result.failed()) {
executeMethodExceptionHandler.handle(result.getException());
}
}

@Override
public String toString() {
return "ToInstantiateHandler for " + type.getName();
Expand Down
86 changes: 86 additions & 0 deletions vraptor-core/src/main/java/br/com/caelum/vraptor/core/Try.java
@@ -0,0 +1,86 @@
package br.com.caelum.vraptor.core;

import java.util.concurrent.Callable;

/**
* A class to wrap code that can possibly throw exceptions.
*
* Use the static method Try#run to instantiate this class, passing down
* the dangerous code and use its methods to retrieve the result or the exception
* of your computation. Example using java 8:
*
* <code>
* Try try = Try.run(() -> someDangerousMethod());
* if (try.failed()) {
* Exception e = try.getException();
* handleError(e);
* }
* try.result(); //do something with the result
* </code>
*
* @author Chico Sokol
* @param <T> the type of the result of your computation
*/
public abstract class Try<T> {
public static <T> Try run(Callable<T> callable) {
try {
T call = callable.call();
return new Success(call);
} catch (Exception e) {
return new Failed(e);
}
}

public abstract boolean failed();

public abstract T result();

public abstract Exception getException();

public static class Success<T> extends Try {
private final T result;

private Success(T result) {
this.result = result;
}

@Override
public boolean failed() {
return false;
}

@Override
public Object result() {
return result;
}

@Override
public Exception getException() {
throw new UnsupportedOperationException("A Success doesn't have an exception.");
}
}

public static class Failed<T> extends Try {
private final Exception e;

private Failed(Exception e) {
this.e = e;
}

@Override
public boolean failed() {
return true;
}

@Override
public Object result() {
throw new UnsupportedOperationException("A Failed doesn't have a result.");
}

@Override
public Exception getException() {
return e;
}
}

}
Expand Up @@ -17,31 +17,27 @@

package br.com.caelum.vraptor.observer;

import static org.slf4j.LoggerFactory.getLogger;

import java.lang.reflect.Method;

import javax.enterprise.context.Dependent;
import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.inject.Inject;

import org.slf4j.Logger;

import br.com.caelum.vraptor.InterceptionException;
import br.com.caelum.vraptor.controller.ControllerMethod;
import br.com.caelum.vraptor.core.MethodInfo;
import br.com.caelum.vraptor.core.ReflectionProvider;
import br.com.caelum.vraptor.core.ReflectionProviderException;
import br.com.caelum.vraptor.core.Try;
import br.com.caelum.vraptor.events.InterceptorsExecuted;
import br.com.caelum.vraptor.events.MethodExecuted;
import br.com.caelum.vraptor.events.MethodReady;
import br.com.caelum.vraptor.interceptor.ApplicationLogicException;
import br.com.caelum.vraptor.validator.Messages;
import br.com.caelum.vraptor.validator.ValidationException;
import org.slf4j.Logger;

import javax.enterprise.context.Dependent;
import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;

import static org.slf4j.LoggerFactory.getLogger;

/**
* Interceptor that executes the logic method.
* Observer that executes the logic method.
*
* @author Guilherme Silveira
* @author Rodrigo Turini
Expand All @@ -58,49 +54,44 @@ public class ExecuteMethod {

private final Event<MethodExecuted> methodExecutedEvent;
private final Event<MethodReady> methodReady;
private final ExecuteMethodExceptionHandler executeMethodExceptionHandler;

@Inject
public ExecuteMethod(MethodInfo methodInfo, Messages messages, Event<MethodExecuted> methodExecutedEvent,
Event<MethodReady> methodReady, ReflectionProvider reflectionProvider) {
public ExecuteMethod(MethodInfo methodInfo, Messages messages,
Event<MethodExecuted> methodExecutedEvent, Event<MethodReady> methodReady,
ExecuteMethodExceptionHandler exceptionHandler, ReflectionProvider reflectionProvider) {
this.methodInfo = methodInfo;
this.messages = messages;
this.methodExecutedEvent = methodExecutedEvent;
this.methodReady = methodReady;
this.executeMethodExceptionHandler = exceptionHandler;
this.reflectionProvider = reflectionProvider;
}

public void execute(@Observes InterceptorsExecuted event) {
try {
ControllerMethod method = event.getControllerMethod();
methodReady.fire(new MethodReady(method));
Method reflectionMethod = method .getMethod();
Object[] parameters = methodInfo.getParametersValues();

log.debug("Invoking {}", reflectionMethod);
Object instance = event.getControllerInstance();
Object result = reflectionProvider.invoke(instance, reflectionMethod, parameters);

messages.assertAbsenceOfErrors();

this.methodInfo.setResult(result);
methodExecutedEvent.fire(new MethodExecuted(method, methodInfo));
} catch (IllegalArgumentException e) {
throw new InterceptionException(e);
} catch (ReflectionProviderException e) {
throwIfNotValidationException(e, e.getCause());
} catch (Exception e) {
throwIfNotValidationException(e, e);
public void execute(@Observes final InterceptorsExecuted event) {
Try run = Try.run(new Callable<Void>() {
@Override
public Void call() throws Exception {
ControllerMethod method = event.getControllerMethod();
methodReady.fire(new MethodReady(method));
Method reflectionMethod = method.getMethod();
Object[] parameters = methodInfo.getParametersValues();

log.debug("Invoking {}", reflectionMethod);
Object instance = event.getControllerInstance();
Object result = reflectionProvider.invoke(instance, reflectionMethod, parameters);

messages.assertAbsenceOfErrors();

methodInfo.setResult(result);
methodExecutedEvent.fire(new MethodExecuted(method, methodInfo));
return null;
}
});
if (run.failed()) {
Exception exception = run.getException();
executeMethodExceptionHandler.handle(exception);
}
}

private void throwIfNotValidationException(Throwable original, Throwable alternativeCause) {
Throwable cause = original.getCause();

if (original instanceof ValidationException || cause instanceof ValidationException) {
// fine... already parsed
log.trace("swallowing {}", cause);
} else {
throw new ApplicationLogicException(alternativeCause);
}
}
}
@@ -0,0 +1,39 @@
package br.com.caelum.vraptor.observer;

import br.com.caelum.vraptor.InterceptionException;
import br.com.caelum.vraptor.core.ReflectionProviderException;
import br.com.caelum.vraptor.interceptor.ApplicationLogicException;
import br.com.caelum.vraptor.validator.ValidationException;
import org.slf4j.Logger;

import static org.slf4j.LoggerFactory.getLogger;

/**
* Handles exceptions thrown by a controller method
*
* @author Chico Sokol
*/
public class ExecuteMethodExceptionHandler {
private final static Logger log = getLogger(ExecuteMethodExceptionHandler.class);

public void handle(Exception exception) {
if (exception instanceof IllegalArgumentException) {
throw new InterceptionException(exception);
}
if (exception instanceof ReflectionProviderException) {
throwIfNotValidationException(exception, exception.getCause());
}
throwIfNotValidationException(exception, exception);
}

private void throwIfNotValidationException(Throwable original, Throwable alternativeCause) {
Throwable cause = original.getCause();

if (original instanceof ValidationException || cause instanceof ValidationException) {
// fine... already parsed
log.trace("swallowing {}", cause);
} else {
throw new ApplicationLogicException(alternativeCause);
}
}
}
Expand Up @@ -20,6 +20,7 @@
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertThat;

import br.com.caelum.vraptor.observer.ExecuteMethodExceptionHandler;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
Expand Down Expand Up @@ -55,8 +56,9 @@ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);

CacheStore<Class<?>, InterceptorHandler> cachedHandlers = new DefaultCacheStore<>();
ExecuteMethodExceptionHandler executeMethodExceptionHandler = new ExecuteMethodExceptionHandler();
factory = new DefaultInterceptorHandlerFactory(container, stepInvoker,
cachedHandlers, acceptsExecutor, customAcceptsExecutor, interceptorExecutor);
cachedHandlers, acceptsExecutor, customAcceptsExecutor, interceptorExecutor, executeMethodExceptionHandler);
}

static interface RegularInterceptor extends Interceptor {}
Expand Down

0 comments on commit 3268b17

Please sign in to comment.