Skip to content

Commit 3268b17

Browse files
committed
Merge pull request #873 from csokol/catching-interceptors-validation-errors
Handling ValidationExceptions in interceptors
2 parents 2f884e2 + 9503b57 commit 3268b17

File tree

11 files changed

+292
-99
lines changed

11 files changed

+292
-99
lines changed

vraptor-core/src/main/java/br/com/caelum/vraptor/core/DefaultInterceptorHandlerFactory.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import br.com.caelum.vraptor.interceptor.StepInvoker;
2929
import br.com.caelum.vraptor.ioc.Container;
3030

31+
import br.com.caelum.vraptor.observer.ExecuteMethodExceptionHandler;
3132
import com.google.common.base.Supplier;
3233

3334
/**
@@ -44,25 +45,28 @@ public class DefaultInterceptorHandlerFactory implements InterceptorHandlerFacto
4445
private final InterceptorAcceptsExecutor acceptsExecutor;
4546
private final CustomAcceptsExecutor customAcceptsExecutor;
4647
private final InterceptorExecutor interceptorExecutor;
48+
private final ExecuteMethodExceptionHandler executeMethodExceptionHandler;
4749

4850
/**
4951
* @deprecated CDI eyes only
5052
*/
5153
protected DefaultInterceptorHandlerFactory() {
52-
this(null, null, null, null, null, null);
54+
this(null, null, null, null, null, null, null);
5355
}
5456

5557
@Inject
5658
public DefaultInterceptorHandlerFactory(Container container, StepInvoker stepInvoker,
5759
CacheStore<Class<?>, InterceptorHandler> cachedHandlers, InterceptorAcceptsExecutor acceptsExecutor,
58-
CustomAcceptsExecutor customAcceptsExecutor, InterceptorExecutor interceptorExecutor) {
60+
CustomAcceptsExecutor customAcceptsExecutor, InterceptorExecutor interceptorExecutor,
61+
ExecuteMethodExceptionHandler executeMethodExceptionHandler) {
5962

6063
this.container = container;
6164
this.stepInvoker = stepInvoker;
6265
this.cachedHandlers = cachedHandlers;
6366
this.acceptsExecutor = acceptsExecutor;
6467
this.customAcceptsExecutor = customAcceptsExecutor;
6568
this.interceptorExecutor = interceptorExecutor;
69+
this.executeMethodExceptionHandler = executeMethodExceptionHandler;
6670
}
6771

6872
@Override
@@ -74,7 +78,7 @@ public InterceptorHandler get() {
7478
return new AspectStyleInterceptorHandler(type, stepInvoker, container, customAcceptsExecutor,
7579
acceptsExecutor, interceptorExecutor);
7680
}
77-
return new ToInstantiateInterceptorHandler(container, type);
81+
return new ToInstantiateInterceptorHandler(container, type, executeMethodExceptionHandler);
7882
}
7983
});
8084
}

vraptor-core/src/main/java/br/com/caelum/vraptor/core/ToInstantiateInterceptorHandler.java

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@
1717

1818
package br.com.caelum.vraptor.core;
1919

20-
import javax.enterprise.inject.Vetoed;
21-
22-
import org.slf4j.Logger;
23-
import org.slf4j.LoggerFactory;
24-
2520
import br.com.caelum.vraptor.InterceptionException;
2621
import br.com.caelum.vraptor.controller.ControllerMethod;
2722
import br.com.caelum.vraptor.interceptor.Interceptor;
2823
import br.com.caelum.vraptor.ioc.Container;
24+
import br.com.caelum.vraptor.observer.ExecuteMethodExceptionHandler;
25+
import org.slf4j.Logger;
26+
import org.slf4j.LoggerFactory;
27+
28+
import javax.enterprise.inject.Vetoed;
29+
import java.util.concurrent.Callable;
2930

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

4041
private final Container container;
4142
private final Class<?> type;
43+
private final ExecuteMethodExceptionHandler executeMethodExceptionHandler;
4244

43-
public ToInstantiateInterceptorHandler(Container container, Class<?> type) {
45+
public ToInstantiateInterceptorHandler(Container container, Class<?> type, ExecuteMethodExceptionHandler executeMethodExceptionHandler) {
4446
this.container = container;
4547
this.type = type;
48+
this.executeMethodExceptionHandler = executeMethodExceptionHandler;
4649
}
4750

4851
@Override
49-
public void execute(InterceptorStack stack, ControllerMethod method, Object controllerInstance)
52+
public void execute(final InterceptorStack stack, final ControllerMethod method, final Object controllerInstance)
5053
throws InterceptionException {
51-
Interceptor interceptor = (Interceptor) container.instanceFor(type);
54+
final Interceptor interceptor = (Interceptor) container.instanceFor(type);
5255
if (interceptor == null) {
5356
throw new InterceptionException("Unable to instantiate interceptor for " + type.getName()
5457
+ ": the container returned null.");
5558
}
5659
if (interceptor.accepts(method)) {
5760
logger.debug("Invoking interceptor {}", interceptor.getClass().getSimpleName());
58-
interceptor.intercept(stack, method, controllerInstance);
61+
executeSafely(stack, method, controllerInstance, interceptor);
5962
} else {
6063
stack.next(method, controllerInstance);
6164
}
6265
}
6366

67+
private void executeSafely(final InterceptorStack stack, final ControllerMethod method, final Object controllerInstance, final Interceptor interceptor) {
68+
Try result = Try.run(new Callable<Void>() {
69+
@Override
70+
public Void call() throws Exception {
71+
interceptor.intercept(stack, method, controllerInstance);
72+
return null;
73+
}
74+
});
75+
if (result.failed()) {
76+
executeMethodExceptionHandler.handle(result.getException());
77+
}
78+
}
79+
6480
@Override
6581
public String toString() {
6682
return "ToInstantiateHandler for " + type.getName();
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package br.com.caelum.vraptor.core;
2+
3+
import java.util.concurrent.Callable;
4+
5+
/**
6+
* A class to wrap code that can possibly throw exceptions.
7+
*
8+
* Use the static method Try#run to instantiate this class, passing down
9+
* the dangerous code and use its methods to retrieve the result or the exception
10+
* of your computation. Example using java 8:
11+
*
12+
* <code>
13+
* Try try = Try.run(() -> someDangerousMethod());
14+
* if (try.failed()) {
15+
* Exception e = try.getException();
16+
* handleError(e);
17+
* }
18+
* try.result(); //do something with the result
19+
* </code>
20+
*
21+
* @author Chico Sokol
22+
* @param <T> the type of the result of your computation
23+
*/
24+
public abstract class Try<T> {
25+
public static <T> Try run(Callable<T> callable) {
26+
try {
27+
T call = callable.call();
28+
return new Success(call);
29+
} catch (Exception e) {
30+
return new Failed(e);
31+
}
32+
}
33+
34+
public abstract boolean failed();
35+
36+
public abstract T result();
37+
38+
public abstract Exception getException();
39+
40+
public static class Success<T> extends Try {
41+
private final T result;
42+
43+
private Success(T result) {
44+
this.result = result;
45+
}
46+
47+
@Override
48+
public boolean failed() {
49+
return false;
50+
}
51+
52+
@Override
53+
public Object result() {
54+
return result;
55+
}
56+
57+
@Override
58+
public Exception getException() {
59+
throw new UnsupportedOperationException("A Success doesn't have an exception.");
60+
}
61+
}
62+
63+
public static class Failed<T> extends Try {
64+
private final Exception e;
65+
66+
private Failed(Exception e) {
67+
this.e = e;
68+
}
69+
70+
@Override
71+
public boolean failed() {
72+
return true;
73+
}
74+
75+
@Override
76+
public Object result() {
77+
throw new UnsupportedOperationException("A Failed doesn't have a result.");
78+
}
79+
80+
@Override
81+
public Exception getException() {
82+
return e;
83+
}
84+
}
85+
86+
}

vraptor-core/src/main/java/br/com/caelum/vraptor/observer/ExecuteMethod.java

Lines changed: 40 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,27 @@
1717

1818
package br.com.caelum.vraptor.observer;
1919

20-
import static org.slf4j.LoggerFactory.getLogger;
21-
22-
import java.lang.reflect.Method;
23-
24-
import javax.enterprise.context.Dependent;
25-
import javax.enterprise.event.Event;
26-
import javax.enterprise.event.Observes;
27-
import javax.inject.Inject;
28-
29-
import org.slf4j.Logger;
30-
31-
import br.com.caelum.vraptor.InterceptionException;
3220
import br.com.caelum.vraptor.controller.ControllerMethod;
3321
import br.com.caelum.vraptor.core.MethodInfo;
3422
import br.com.caelum.vraptor.core.ReflectionProvider;
35-
import br.com.caelum.vraptor.core.ReflectionProviderException;
23+
import br.com.caelum.vraptor.core.Try;
3624
import br.com.caelum.vraptor.events.InterceptorsExecuted;
3725
import br.com.caelum.vraptor.events.MethodExecuted;
3826
import br.com.caelum.vraptor.events.MethodReady;
39-
import br.com.caelum.vraptor.interceptor.ApplicationLogicException;
4027
import br.com.caelum.vraptor.validator.Messages;
41-
import br.com.caelum.vraptor.validator.ValidationException;
28+
import org.slf4j.Logger;
29+
30+
import javax.enterprise.context.Dependent;
31+
import javax.enterprise.event.Event;
32+
import javax.enterprise.event.Observes;
33+
import javax.inject.Inject;
34+
import java.lang.reflect.Method;
35+
import java.util.concurrent.Callable;
36+
37+
import static org.slf4j.LoggerFactory.getLogger;
4238

4339
/**
44-
* Interceptor that executes the logic method.
40+
* Observer that executes the logic method.
4541
*
4642
* @author Guilherme Silveira
4743
* @author Rodrigo Turini
@@ -58,49 +54,44 @@ public class ExecuteMethod {
5854

5955
private final Event<MethodExecuted> methodExecutedEvent;
6056
private final Event<MethodReady> methodReady;
57+
private final ExecuteMethodExceptionHandler executeMethodExceptionHandler;
6158

6259
@Inject
63-
public ExecuteMethod(MethodInfo methodInfo, Messages messages, Event<MethodExecuted> methodExecutedEvent,
64-
Event<MethodReady> methodReady, ReflectionProvider reflectionProvider) {
60+
public ExecuteMethod(MethodInfo methodInfo, Messages messages,
61+
Event<MethodExecuted> methodExecutedEvent, Event<MethodReady> methodReady,
62+
ExecuteMethodExceptionHandler exceptionHandler, ReflectionProvider reflectionProvider) {
6563
this.methodInfo = methodInfo;
6664
this.messages = messages;
6765
this.methodExecutedEvent = methodExecutedEvent;
6866
this.methodReady = methodReady;
67+
this.executeMethodExceptionHandler = exceptionHandler;
6968
this.reflectionProvider = reflectionProvider;
7069
}
7170

72-
public void execute(@Observes InterceptorsExecuted event) {
73-
try {
74-
ControllerMethod method = event.getControllerMethod();
75-
methodReady.fire(new MethodReady(method));
76-
Method reflectionMethod = method .getMethod();
77-
Object[] parameters = methodInfo.getParametersValues();
78-
79-
log.debug("Invoking {}", reflectionMethod);
80-
Object instance = event.getControllerInstance();
81-
Object result = reflectionProvider.invoke(instance, reflectionMethod, parameters);
82-
83-
messages.assertAbsenceOfErrors();
84-
85-
this.methodInfo.setResult(result);
86-
methodExecutedEvent.fire(new MethodExecuted(method, methodInfo));
87-
} catch (IllegalArgumentException e) {
88-
throw new InterceptionException(e);
89-
} catch (ReflectionProviderException e) {
90-
throwIfNotValidationException(e, e.getCause());
91-
} catch (Exception e) {
92-
throwIfNotValidationException(e, e);
71+
public void execute(@Observes final InterceptorsExecuted event) {
72+
Try run = Try.run(new Callable<Void>() {
73+
@Override
74+
public Void call() throws Exception {
75+
ControllerMethod method = event.getControllerMethod();
76+
methodReady.fire(new MethodReady(method));
77+
Method reflectionMethod = method.getMethod();
78+
Object[] parameters = methodInfo.getParametersValues();
79+
80+
log.debug("Invoking {}", reflectionMethod);
81+
Object instance = event.getControllerInstance();
82+
Object result = reflectionProvider.invoke(instance, reflectionMethod, parameters);
83+
84+
messages.assertAbsenceOfErrors();
85+
86+
methodInfo.setResult(result);
87+
methodExecutedEvent.fire(new MethodExecuted(method, methodInfo));
88+
return null;
89+
}
90+
});
91+
if (run.failed()) {
92+
Exception exception = run.getException();
93+
executeMethodExceptionHandler.handle(exception);
9394
}
9495
}
9596

96-
private void throwIfNotValidationException(Throwable original, Throwable alternativeCause) {
97-
Throwable cause = original.getCause();
98-
99-
if (original instanceof ValidationException || cause instanceof ValidationException) {
100-
// fine... already parsed
101-
log.trace("swallowing {}", cause);
102-
} else {
103-
throw new ApplicationLogicException(alternativeCause);
104-
}
105-
}
10697
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package br.com.caelum.vraptor.observer;
2+
3+
import br.com.caelum.vraptor.InterceptionException;
4+
import br.com.caelum.vraptor.core.ReflectionProviderException;
5+
import br.com.caelum.vraptor.interceptor.ApplicationLogicException;
6+
import br.com.caelum.vraptor.validator.ValidationException;
7+
import org.slf4j.Logger;
8+
9+
import static org.slf4j.LoggerFactory.getLogger;
10+
11+
/**
12+
* Handles exceptions thrown by a controller method
13+
*
14+
* @author Chico Sokol
15+
*/
16+
public class ExecuteMethodExceptionHandler {
17+
private final static Logger log = getLogger(ExecuteMethodExceptionHandler.class);
18+
19+
public void handle(Exception exception) {
20+
if (exception instanceof IllegalArgumentException) {
21+
throw new InterceptionException(exception);
22+
}
23+
if (exception instanceof ReflectionProviderException) {
24+
throwIfNotValidationException(exception, exception.getCause());
25+
}
26+
throwIfNotValidationException(exception, exception);
27+
}
28+
29+
private void throwIfNotValidationException(Throwable original, Throwable alternativeCause) {
30+
Throwable cause = original.getCause();
31+
32+
if (original instanceof ValidationException || cause instanceof ValidationException) {
33+
// fine... already parsed
34+
log.trace("swallowing {}", cause);
35+
} else {
36+
throw new ApplicationLogicException(alternativeCause);
37+
}
38+
}
39+
}

vraptor-core/src/test/java/br/com/caelum/vraptor/core/DefaultInterceptorHandlerFactoryTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static org.hamcrest.Matchers.sameInstance;
2121
import static org.junit.Assert.assertThat;
2222

23+
import br.com.caelum.vraptor.observer.ExecuteMethodExceptionHandler;
2324
import org.junit.Before;
2425
import org.junit.Test;
2526
import org.mockito.Mock;
@@ -55,8 +56,9 @@ public void setUp() throws Exception {
5556
MockitoAnnotations.initMocks(this);
5657

5758
CacheStore<Class<?>, InterceptorHandler> cachedHandlers = new DefaultCacheStore<>();
59+
ExecuteMethodExceptionHandler executeMethodExceptionHandler = new ExecuteMethodExceptionHandler();
5860
factory = new DefaultInterceptorHandlerFactory(container, stepInvoker,
59-
cachedHandlers, acceptsExecutor, customAcceptsExecutor, interceptorExecutor);
61+
cachedHandlers, acceptsExecutor, customAcceptsExecutor, interceptorExecutor, executeMethodExceptionHandler);
6062
}
6163

6264
static interface RegularInterceptor extends Interceptor {}

0 commit comments

Comments
 (0)