ItEr20S04ArquitecturaServidorItEr19S04: Adding Exception catcher proxy.
This commit is contained in:
parent
e4241f4490
commit
6b2a028b01
2 changed files with 265 additions and 0 deletions
|
|
@ -0,0 +1,115 @@
|
|||
package org.navalplanner.web.common;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
|
||||
public class ExceptionCatcherProxy<T> {
|
||||
|
||||
public interface IExceptionHandler<T extends Exception> {
|
||||
public void onException(T exception);
|
||||
}
|
||||
|
||||
public static <T> ExceptionCatcherProxy<T> doCatchFor(
|
||||
Class<T> interfaceKlass) {
|
||||
return new ExceptionCatcherProxy<T>(interfaceKlass);
|
||||
}
|
||||
|
||||
private static class RegisteredHandler<T extends Exception> {
|
||||
private final Class<T> exceptionClass;
|
||||
|
||||
private final IExceptionHandler<T> handler;
|
||||
|
||||
RegisteredHandler(Class<T> exceptionClass, IExceptionHandler<T> handler) {
|
||||
this.exceptionClass = exceptionClass;
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
void invoke(Exception e) {
|
||||
handler.onException(exceptionClass.cast(e));
|
||||
}
|
||||
|
||||
boolean isAppyable(Throwable cause) {
|
||||
return exceptionClass.isInstance(cause);
|
||||
}
|
||||
|
||||
public boolean isMoreSpecificThan(RegisteredHandler<?> r) {
|
||||
return r.exceptionClass.isAssignableFrom(this.exceptionClass);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private List<RegisteredHandler<?>> handlers = new LinkedList<RegisteredHandler<?>>();
|
||||
private final Class<T> interfaceKlass;
|
||||
|
||||
private ExceptionCatcherProxy(Class<T> interfaceKlass) {
|
||||
this.interfaceKlass = interfaceKlass;
|
||||
Validate.isTrue(interfaceKlass.isInterface());
|
||||
}
|
||||
|
||||
public T applyTo(final T instance) {
|
||||
return interfaceKlass.cast(Proxy.newProxyInstance(instance.getClass()
|
||||
.getClassLoader(), new Class[] { interfaceKlass },
|
||||
new InvocationHandler() {
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method,
|
||||
Object[] args) throws Throwable {
|
||||
Object result;
|
||||
try {
|
||||
result = method.invoke(instance, args);
|
||||
return result;
|
||||
} catch (InvocationTargetException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (!handled(cause)) {
|
||||
throw cause;
|
||||
}
|
||||
// we don't know what would be the result, so we
|
||||
// return null
|
||||
return null;
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
private boolean handled(Throwable cause) {
|
||||
if (!(cause instanceof Exception)) {
|
||||
return false;
|
||||
}
|
||||
Exception exception = (Exception) cause;
|
||||
for (RegisteredHandler<?> registeredHandler : handlers) {
|
||||
if (registeredHandler.isAppyable(cause)) {
|
||||
registeredHandler.invoke(exception);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public <E extends Exception> ExceptionCatcherProxy<T> when(
|
||||
Class<E> exception, IExceptionHandler<E> handler) {
|
||||
RegisteredHandler<E> registered = new RegisteredHandler<E>(exception,
|
||||
handler);
|
||||
insertAtRightPosition(registered);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void insertAtRightPosition(RegisteredHandler<?> handler) {
|
||||
int i = 0;
|
||||
for (RegisteredHandler<?> r : handlers) {
|
||||
if (handler.isMoreSpecificThan(r)) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
handlers.add(i, handler);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
package org.navalplanner.web.common;
|
||||
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.easymock.EasyMock;
|
||||
import org.junit.Test;
|
||||
import org.navalplanner.web.common.ExceptionCatcherProxy.IExceptionHandler;
|
||||
|
||||
public class ExceptionCatcherProxyTest {
|
||||
|
||||
private final class Dummy implements IDummy {
|
||||
@Override
|
||||
public void doFoo() {
|
||||
throw new IllegalArgumentException("bla");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doBar() {
|
||||
throw new IllegalStateException("bla");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHello() {
|
||||
throw new RuntimeException("bla");
|
||||
}
|
||||
}
|
||||
|
||||
public interface IDummy {
|
||||
void doFoo();
|
||||
|
||||
String getHello();
|
||||
|
||||
void doBar();
|
||||
|
||||
}
|
||||
|
||||
private IDummy dummy;
|
||||
|
||||
public ExceptionCatcherProxyTest() {
|
||||
dummy = new Dummy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anAutomaticExceptionCatcherWrapsAnObjectImplementingAnInterface() {
|
||||
IDummy dummyMock = EasyMock.createMock(IDummy.class);
|
||||
dummyMock.doBar();
|
||||
dummyMock.doFoo();
|
||||
expect(dummyMock.getHello()).andReturn("hi");
|
||||
IDummy proxified = ExceptionCatcherProxy.doCatchFor(IDummy.class)
|
||||
.applyTo(dummyMock);
|
||||
assertNotNull(proxified);
|
||||
EasyMock.replay(dummyMock);
|
||||
proxified.doBar();
|
||||
proxified.doFoo();
|
||||
assertThat(proxified.getHello(), equalTo("hi"));
|
||||
verify(dummyMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theSameExceptionsAreThrownIfNotHandlersRegistered() {
|
||||
IDummy proxified = ExceptionCatcherProxy.doCatchFor(IDummy.class)
|
||||
.applyTo(dummy);
|
||||
|
||||
try {
|
||||
proxified.doBar();
|
||||
fail("must throw IllegalStateException");
|
||||
} catch (IllegalStateException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
try {
|
||||
proxified.doFoo();
|
||||
fail("must throw IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// ok
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void itLetsToSpecifyWhatToDoForSomeExceptions() {
|
||||
final boolean[] onExceptionCalled = { false };
|
||||
IDummy proxified = ExceptionCatcherProxy.doCatchFor(IDummy.class).when(
|
||||
IllegalArgumentException.class,
|
||||
new IExceptionHandler<IllegalArgumentException>() {
|
||||
|
||||
@Override
|
||||
public void onException(IllegalArgumentException exception) {
|
||||
onExceptionCalled[0] = true;
|
||||
}
|
||||
}).applyTo(dummy);
|
||||
proxified.doFoo();
|
||||
assertTrue(onExceptionCalled[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theMostSpecificHandlerIsUsed() {
|
||||
final boolean[] runtimeExceptionCalled = { false };
|
||||
IDummy proxified = ExceptionCatcherProxy.doCatchFor(IDummy.class).when(
|
||||
RuntimeException.class,
|
||||
new IExceptionHandler<RuntimeException>() {
|
||||
|
||||
@Override
|
||||
public void onException(RuntimeException exception) {
|
||||
runtimeExceptionCalled[0] = true;
|
||||
}
|
||||
}).when(IllegalArgumentException.class,
|
||||
new IExceptionHandler<IllegalArgumentException>() {
|
||||
|
||||
@Override
|
||||
public void onException(IllegalArgumentException exception) {
|
||||
// do nothing
|
||||
|
||||
}
|
||||
}).applyTo(dummy);
|
||||
proxified.doFoo();
|
||||
assertFalse(runtimeExceptionCalled[0]);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ifTheExceptionHandlerDontThrowExceptionReturnsNull() {
|
||||
IDummy dummyProxified = ExceptionCatcherProxy.doCatchFor(IDummy.class)
|
||||
.when(RuntimeException.class,
|
||||
new IExceptionHandler<RuntimeException>() {
|
||||
|
||||
@Override
|
||||
public void onException(RuntimeException exception) {
|
||||
// do nothing
|
||||
}
|
||||
}).applyTo(dummy);
|
||||
try {
|
||||
dummy.getHello();
|
||||
fail("it throws error");
|
||||
} catch (RuntimeException e) {
|
||||
// ok
|
||||
}
|
||||
assertNull(dummyProxified.getHello());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue