From 6b2a028b01ddaba9e4d2af7294166cad5391d21e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93scar=20Gonz=C3=A1lez=20Fern=C3=A1ndez?= Date: Fri, 7 Aug 2009 17:06:15 +0200 Subject: [PATCH] ItEr20S04ArquitecturaServidorItEr19S04: Adding Exception catcher proxy. --- .../web/common/ExceptionCatcherProxy.java | 115 ++++++++++++++ .../web/common/ExceptionCatcherProxyTest.java | 150 ++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 navalplanner-webapp/src/main/java/org/navalplanner/web/common/ExceptionCatcherProxy.java create mode 100644 navalplanner-webapp/src/test/java/org/navalplanner/web/common/ExceptionCatcherProxyTest.java diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ExceptionCatcherProxy.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ExceptionCatcherProxy.java new file mode 100644 index 000000000..84629729d --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ExceptionCatcherProxy.java @@ -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 { + + public interface IExceptionHandler { + public void onException(T exception); + } + + public static ExceptionCatcherProxy doCatchFor( + Class interfaceKlass) { + return new ExceptionCatcherProxy(interfaceKlass); + } + + private static class RegisteredHandler { + private final Class exceptionClass; + + private final IExceptionHandler handler; + + RegisteredHandler(Class exceptionClass, IExceptionHandler 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> handlers = new LinkedList>(); + private final Class interfaceKlass; + + private ExceptionCatcherProxy(Class 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 ExceptionCatcherProxy when( + Class exception, IExceptionHandler handler) { + RegisteredHandler registered = new RegisteredHandler(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); + } + +} diff --git a/navalplanner-webapp/src/test/java/org/navalplanner/web/common/ExceptionCatcherProxyTest.java b/navalplanner-webapp/src/test/java/org/navalplanner/web/common/ExceptionCatcherProxyTest.java new file mode 100644 index 000000000..af14d0af5 --- /dev/null +++ b/navalplanner-webapp/src/test/java/org/navalplanner/web/common/ExceptionCatcherProxyTest.java @@ -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() { + + @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() { + + @Override + public void onException(RuntimeException exception) { + runtimeExceptionCalled[0] = true; + } + }).when(IllegalArgumentException.class, + new IExceptionHandler() { + + @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() { + + @Override + public void onException(RuntimeException exception) { + // do nothing + } + }).applyTo(dummy); + try { + dummy.getHello(); + fail("it throws error"); + } catch (RuntimeException e) { + // ok + } + assertNull(dummyProxified.getHello()); + + } + +}