blob: 47828b5e989b7c5d07921be399c79b00579f4897 [file] [log] [blame]
= Multiple Business Interface Hazzards
:index-group: Unrevised
:jbake-date: 2018-12-05
:jbake-type: page
:jbake-status: published
= UndeclaredThrowableException
When two java interfaces are implemented by a proxy and those two
interfaces declare the _same method_ but with _different throws clauses_
some very nasty side effects happen, namely you loose the ability to
throw any checked exceptions that are not in the throws clause of both
methods.
[source,java]
----
import junit.framework.TestCase;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
/**
* @version $Rev$ $Date$
*/
public class ExceptionTest extends TestCase {
public void test() throws Exception {
ClassLoader classLoader = this.getClass().getClassLoader();
Class[]
----
interfaces = new Class[]\{One.class, Two.class};
[source,java]
----
InvocationHandler h = new TestInvocationHandler();
Object proxy =
----
java.lang.reflect.Proxy.newProxyInstance(classLoader, interfaces, h);
[source,java]
----
One one = (One) proxy;
try {
one.run(new CommonException());
} catch (CommonException e) {
// this will work
} catch(UndeclaredThrowableException u) {
Throwable t = u.getCause();
fail("Undeclared: "+t);
} catch(Throwable t){
fail("Caught: "+t);
}
try {
one.run(new OneException());
} catch (OneException e) {
} catch(UndeclaredThrowableException u) {
Throwable t = u.getCause();
fail("Undeclared: "+t); // This will always be the code that
----
executes } catch(Throwable t)\{ fail("Caught: "+t); }
[source,java]
----
Two two = (Two) proxy;
try {
two.run(new CommonException());
} catch (TwoException e) {
} catch(UndeclaredThrowableException u) {
Throwable t = u.getCause();
fail("Undeclared: "+t); // This will always be the code that
----
executes } catch(Throwable t)\{ fail("Caught: "+t); }
[source,java]
----
}
public static class CommonException extends Exception {
public CommonException() {
}
}
public static interface One {
void run(Object o) throws OneException, CommonException;
}
public static class OneException extends Exception {
public OneException() {
}
}
public static interface Two {
void run(Object o) throws TwoException, CommonException;
}
public static class TwoException extends Exception {
public TwoException() {
}
}
private static class TestInvocationHandler implements InvocationHandler
----
\{ public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable \{ throw (Throwable)args[0] ; } } }
= IllegalArgumentException
This one is less of a runtime problem as doing this will cause things to
fail up front. When two java interfaces are implemented by a proxy and
those two interfaces declare the _same method_ but with _different
return types_ the VM proxy code will refuse to create a proxy at all.
Take this code example:
[source,java]
----
import junit.framework.TestCase;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @version $Rev$ $Date$
*/
public class ReturnTest extends TestCase {
public void test() throws Exception {
ClassLoader classLoader = this.getClass().getClassLoader();
Class[]
----
interfaces = new Class[]\{ReturnTest.One.class, ReturnTest.Two.class};
[source,java]
----
InvocationHandler h = new ReturnTest.TestInvocationHandler();
Object proxy =
----
java.lang.reflect.Proxy.newProxyInstance(classLoader, interfaces, h);
[source,java]
----
One one = (One) proxy;
try {
Object object = one.run(new ThingOne());
} catch (Throwable t) {
fail("Caught: " + t);
}
Two two = (Two) proxy;
try {
Object object = two.run(new ThingTwo());
} catch (Throwable t) {
fail("Caught: " + t);
}
}
public static interface One {
ThingOne run(Object o);
}
public static class ThingOne {
}
public static interface Two {
ThingTwo run(Object o);
}
public static class ThingTwo {
}
private static class TestInvocationHandler implements InvocationHandler
----
\{ public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable \{ return args[0] ; } } }
Running this code will result in the following exception:
[source,properties]
----
java.lang.IllegalArgumentException: methods with same signature
----
run(java.lang.Object) but incompatible return types: [class
ReturnTestlatexmath:[$ThingOne, class ReturnTest$]ThingTwo] at
sun.misc.ProxyGenerator.checkReturnTypes(ProxyGenerator.java:669) at
sun.misc.ProxyGenerator.generateClassFile(ProxyGenerator.java:420) at
sun.misc.ProxyGenerator.generateProxyClass(ProxyGenerator.java:306) at
java.lang.reflect.Proxy.getProxyClass(Proxy.java:501) at
java.lang.reflect.Proxy.newProxyInstance(Proxy.java:581) at
ReturnTest.test(ReturnTest.java:36) at
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at
com.intellij.rt.execution.junit2.JUnitStarter.main(JUnitStarter.java:32)