Playing around with exception stack traces

Debugging exception stack traces is inevitable for any Java developer. When there is a failure at the customer end, our first reaction would be “Are there any exception stack traces?”. Stack traces play a key role for Java software to provide essential feedback on failures to the end user as well as the developer.

During the development process, there would be a lot of focus on exception handling mechanisms. The intention is to deduce or arrive at the cause for any failure with minimal effort. Nevertheless, there could be situations where you end up debugging or not able to debug bad stack traces which hardly give any information about the cause of the failure. In this post, I would like to focus on these situations and discuss possible solutions to fix such bad stack traces.

Dynamic proxy invocation is a well established pattern in Java. Especially, Containers that provide middle-ware services use this pattern extensively to intercept the calls and embed their services.

Stack traces while invoking Dynamic Proxies

Let us take a look at the invocation of dynamic Proxy instances and the stack traces of exceptions thrown during invocation.

public interface Customer {
    public void login() throws LoginException;
}

public interface Administrator {
    public void login() throws LoginException;
}

public class LoginProvider implements InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        ...
        throw new LoginException("Login failed !!");
        ...
    }

    public static void main(String[] args) throws Exception {
        Customer cust = (Customer)Proxy.newProxyInstance(
                ClassLoader.getSystemClassLoader(),
                new Class[]{Customer.class},
                new LoginProvider());
        cust.login();

        Administrator admin = (Administrator)Proxy.newProxyInstance(
                ClassLoader.getSystemClassLoader(),
                new Class[]{Administrator.class},
                new LoginProvider());
        admin.login();
    }
}

In the above example, dynamic proxy instances are created for Customer and Administrator interfaces, and implementation for login() method is provided by the LoginProvider invocation handler. LoginProvider throws a LoginException when there is any failure during login, as shown below.

Exception in thread "main" proxies.LoginException: Login failed !!
    at proxies.LoginProvider.invoke(LoginProvider.java:18)
    at proxies.$Proxy1.login(Unknown Source)
    at proxies.LoginProvider.main(LoginProvider.java:32)

When you look at the above stack trace, first thing that comes into mind is “why did login fail?” and “who is trying to login?”. You can figure out why login has failed by looking at the exception message or the cause, but you can never get to know whose login has failed. In the above stack trace, you can see that the “$Proxy” masks information about Customer or Administrator interfaces, giving no clue on whose login has failed. Unless we have the source for LoginProvider we can’t deduce the problem, as we need to figure out the whereabouts of proxy’s creation and the interfaces it implements. Now, this becomes even harder if the interfaces implemented by the Proxy are read from configuration (Xml/Annotation etc.).

Tweaking exception stack traces

Many a times though there is enough flexibility in the programming language, we would hardly find a way to make use of it. One such a thing is modifying stack trace of an exception and we can explore this feature to fix our bad stack trace.

It is very simple to modify the stack trace, java.lang.Throwable provides getStackTrace() and setStackTrace() methods to get or set an array of StackTraceElements. So, you can create your own array of these StackTraceElements and set the array back on the Throwable instance. Each StackTraceElement can be instantiated and contain a customized class name, file name, line number etc.

public void setStackTrace(StackTraceElements[] stackTrace) { … }

Understanding the above exception stack trace is really difficult for end users, system administrators or developers. Instead, if we can replace “$Proxy” with useful information like interface name, deployment name or JNDI name then we can quickly relate the problem to the component that has failed.

Exception in thread "main" proxies.LoginException: Login failed !!
    at proxies.LoginProvider.invoke(LoginProvider.java:18)
    at proxies.Customer.login(Unknown Source)
    at proxies.LoginProvider.main(LoginProvider.java:32)

In the above stack trace proxy’s occurrence has been replaced with the interface name it implements. Now, you clearly know whose login has failed by looking at the stack trace. Someone can immediately point out that instead of tweaking the stack trace it would have been better to improve exception messaging and if required wrap the exception to contain relevant information. Yes, actually there is a trade-off between wrapping the cause and tweaking the exception stack trace. The trade-off with the tweaking approach is you can put up a generic stack trace manipulator without much knowledge about the exception. Where as in the other case we need to understand each type of exception and do the necessary wrapping.

Debuggers and Stack traces

Exceptions from a JSP page

How do you debug exceptions thrown when you access a JSP page? Aaahh..! Its a terrible pain to look into the generated servlet java files and relate the failure to the JSP line number. Some of the JSP containers support JSR-45, Java Debugging Support for Other Languages, to map the generated servlet java line numbers to the JSP line numbers and vice versa (through SMAP). This information is embedded into the servlet class file and is useful to debugger tools to track the line numbers of the JSP page back, when the servlet gets executed behind.

SMAP information is actually available to the JVM, but unfortunately VM doesn’t generate the stack trace based on this information. There is a bug reported for this issue and you can vote for this. Hence, JSP Containers need some way of transforming the exception stack traces to contain JSP line numbers, making it more readable to an end user. One of the ways could be the above suggested method to use setStackTrace() to post-process the exception stack trace after it has been raised.

Exceptions from obfuscated code

Another similar use case where we need a readable stack trace is, when the code is Obfuscated. If the obfuscation tools support JSR-45 and provide the obfuscated code to the original code line number mapping through SMAP, then we would have cleaner stack traces along with debugging support for obfuscated code.


Technorati :

Published by Deepak Anupalli

Deepak Anupalli, is a key member of Server Engineering group at Pramati Technologies, he has deep insight into Java, J2EE & web technologies.

4 replies on “Playing around with exception stack traces”

  1. Hi Deepak,

    I am a Development Editor working with Packt Publishing. We are coming out with a book on Amazon Simple DB: Developer’s Guide.
    I am looking for a Technical Reviewer to provide feedback on the content of the book. It would be of great help if you would be willing to take it up as this is your area of expertise.

    Please visit http://www.packtpub.com/author_reviewing_for_packt for more details on reviewing.
    When people like yourself, work for us as technical reviewers, we can be sure that the content will be of the highest quality.
    I should point out that there is no payment for reviewing, instead you will receive full credit in the book (including
    a bio where you can put up a link to your website or blog) and you will receive two complimentary books:
    · One copy of each book you review – see how what you’ve done makes you proud!
    · A book of your choice from our catalog.

    For more details on Packt Publishing, visit http://www.packtpub.com/ . Please let me know if this interests you, so that I can give you more details on technical reviewing and the book.

Comments are closed.