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

The root cause of the above issue is an subtle bug in the InvocationHandler implementation. The invocation handler internally calls java.lang.reflect.Method.invoke method and you’ll see that it can throw an InvocationTargetException. This occurs when the method being reflectively invoked throws any exception.

Since reflectively calling a method could result in a checked exception being thrown but not handled (since the signature of Method.invoke does not declare it), all exceptions thrown by the target are wrapped in a checked InvocationTargetException, which is declared in the signature of Method.invoke.

Normally when calling Method.invoke() the InvocationTargetException must be explicitly handled, since it is a checked exception. However, in the above case we’re calling it from inside an InvocationHandler’s invoke method, whose signature declares that it throws Throwable. Because of this, it is very easy to write an InvocationHandler that throws an InvocationTargetException out of its invoke method (which the above code will do).

Now if you read the documentation for the InvocationHandler.invoke method, you’ll see that it describes how Java dynamic proxies respond to any Throwable thrown out of the InvocationHandler. In particular, if the exception is either a checked exception that is declared by the proxied interface, or is an unchecked exception, it will be propagated directly to the caller of the proxied method. However, if the exception is checked and is not declared by the proxied interface, it will first be wrapped in an UndeclaredThrowableException.

Remembering that InvocationTargetException is a checked exception, what this all boils down to is that any InvocationHandler written as above does not explictly handle the InvocationTargetException from Method.invoke() and will end up propogating an UndeclaredThrowableException to client code. The client code calling the proxied method is hardly ever going to expect this exception.

Given that most of the time, the goal of dynamic proxying is to provide transparent proxying of a service, this situation is hardly going to result in transparency. When client code invokes a proxied method, and the “real” implementation throws any exception (checked or unchecked), that exception should propagated to the calling client code. Any other implementation will result in client code that needs to be aware of the proxying, which means losing one of the main advantages of using dynamic proxies in the first place.

Here’s the correct implementation of the InvocationHandler.invoke() method from the above code:

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");
        try {
            return method.invoke(delegate, args);
        } catch (InvocationTargetException e) {
            throw e.getCause();
        }
    }
}

The reason behind an incorrect exception (UndeclaredThrowableException) being thrown has a lot to do with the checked exception system in Java. A lot of this complexity could have been avoided were it not for the Java language’s checked exception design.

Special thanks to this post for providing a detailed explanation on this topic.

Posted on July 24, 2011, in java and tagged , . Bookmark the permalink. 11 Comments.

  1. Neat. this solved my problem 🙂

  2. Greate article! Thx man!

  3. Thanks, that was spot on and solved my problem.

  4. Spot on article, kudos to you:).

  5. Thanks! Solved a problem for me 🙂

  6. Thanks!

  7. Thanks, simple solution but easy to miss 🙂

  8. Thank you! Very helpful 🙂

  9. A few years late to this party but thanks for a concise explanation and solution.

  10. Avijit Sharma

    Special thanks to “this post” for providing a detailed explanation on this to.

    Link is expired for “this post”

Leave a reply to Vika Cancel reply