Blog Archives

Java Proxies and UndeclaredThrowableException

A common approach in Java is to use dynamic proxies to provide decorator-style behavior. Doing this allows you to add additional behavior “around” an object without the object itself or it’s callers being aware of the decorating.

Let’s take an common implementation of java proxy.

public interface InterfaceA {
    void display() throws SQLException;
}

public class ClassA implements InterfaceA {

    @Override
    public void display() throws SQLException {
        throw new SQLException();
    }
}

public class ProxyHandler implements InvocationHandler {

    private InterfaceA delegate;

    public ProxyHandler(InterfaceA delegate) {
        this.delegate = delegate;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Inside the invocation handler");
        return method.invoke(delegate, args);
    }
}

public class ProxyApp {
    public static void main(String[] args) {
        createAndTestProxy();
    }

    private static void createAndTestProxy() {
        InterfaceA interfaceA = (InterfaceA) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                                                    new Class[]{InterfaceA.class}, new ProxyHandler(new ClassA()));
        try {
            interfaceA.display();
        } catch (java.sql.SQLException e) {
            throw new RuntimeException("Something bad happened", e);
        }
    }
}

If you run the ProxyApp class to test the proxy implementation, you would get the below exception trace

Inside the invocation handler
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
	at $Proxy0.display(Unknown Source)
	at proxy.ProxyApp.createAndTestProxy(ProxyApp.java:23)
	at proxy.ProxyApp.main(ProxyApp.java:16)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:110)
Caused by: java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at proxy.ProxyHandler.invoke(ProxyHandler.java:26)
	... 8 more
Caused by: java.sql.SQLException
	at proxy.ClassA.display(ClassA.java:18)
	... 13 more

The expected stacktrace should have been

Inside the invocation handler
Exception in thread "main" java.lang.RuntimeException: Something bad happened
	at proxy.ProxyApp.createAndTestProxy(ProxyApp.java:25)
	at proxy.ProxyApp.main(ProxyApp.java:16)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:110)
Caused by: java.sql.SQLException
	at proxy.ClassA.display(ClassA.java:18)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at proxy.ProxyHandler.invoke(ProxyHandler.java:28)
	at $Proxy0.display(Unknown Source)
	at proxy.ProxyApp.createAndTestProxy(ProxyApp.java:23)
	... 6 more

Read the rest of this entry