ItEr20S04ArquitecturaServidorItEr19S04: Adding Exception catcher proxy.

This commit is contained in:
Óscar González Fernández 2009-08-07 17:06:15 +02:00
parent e4241f4490
commit 6b2a028b01
2 changed files with 265 additions and 0 deletions

View file

@ -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);
}
}

View file

@ -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());
}
}