be.wegenenverkeer.common.resteasy.exception.ExceptionUtil.java Source code

Java tutorial

Introduction

Here is the source code for be.wegenenverkeer.common.resteasy.exception.ExceptionUtil.java

Source

/*
 * This file is part of wegenenverkeer common-resteasy.
 * Copyright (c) AWV Agentschap Wegen en Verkeer, Vlaamse Gemeenschap
 * The program is available in open source according to the Apache License, Version 2.0.
 * For full licensing details, see LICENSE.txt in the project root.
 */

package be.wegenenverkeer.common.resteasy.exception;

import java.io.InvalidClassException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Locale;

/**
 * ExceptionUtil allows you to get more information about an exception, without having to investigate the causes.
 * <p/>
 * Originally from equanda, http://equanda.svn.sourceforge.net/, class org/equanda/util/ExceptionUtil.java.
 */
public class ExceptionUtil {

    private static final String[] FILTER_PREFIX = { "java.lang.reflect.Method", "org.apache.catalina",
            "org.eclipse.jetty", "org.springframework.aop", "org.springframework.cglib",
            "org.springframework.security", "org.springframework.transaction", "org.springframework.web",
            "sun.reflect", "net.sf.cglib", };
    private static final String[] FILTER_CONTAINS = { "ByCGLIB$$", };

    private boolean isRetryable;
    private boolean isRecoverable;
    private String shortMessage;
    private String concatenatedMessage;
    private Throwable originalException;

    /**
     * Construct instance which allows investigating the exception.
     *
     * @param originalException exception to investigate
     */
    public ExceptionUtil(Throwable originalException) {
        this.originalException = originalException;
        isRetryable = false;
        isRecoverable = true;
        Throwable exc = originalException;
        Throwable lastCause = exc;
        StringBuilder concat = new StringBuilder();

        if (exc instanceof InvocationTargetException && null != exc.getCause()) {
            exc = exc.getCause();
        }

        for (Throwable c = exc; c != null; c = getCause(c)) {
            if (concat.length() > 0) {
                concat.append("; ");
            }
            concat.append(getMessage(c));

            String cn = c.getClass().getName().toLowerCase(Locale.ENGLISH);
            String em = c.getMessage();
            if (em == null) {
                em = "";
            }
            em = em.toLowerCase(Locale.ENGLISH);
            if (c instanceof java.rmi.UnmarshalException || c instanceof InvalidClassException
                    || c instanceof ClassNotFoundException || c instanceof LinkageError
                    || c instanceof VirtualMachineError || c instanceof IllegalAccessException) {
                isRecoverable = false;
            } else {
                if (cn.contains("deadlock") || em.contains("deadlock") || cn.contains("staleobjectexception")
                        || em.contains("staleobjectexception") || cn.contains("sockettimeoutexception")
                        || em.contains("sockettimeoutexception") || cn.contains("communicationexception")
                        || em.contains("communicationexception")
                        || cn.contains("concurrentmodificationexception")) {
                    isRetryable = true;
                }
            }
        }

        concatenatedMessage = concat.toString();
        shortMessage = getMessage(lastCause);
    }

    /**
     * Is the exception recoverable?
     *
     * @return is it likely that the error can be recovered from, or should we exit?
     */
    public boolean isRecoverable() {
        return isRecoverable;
    }

    /**
     * Can the operation which caused the exception be retried?
     *
     * @return would it be useful to retry the operation?
     */
    public boolean isRetryable() {
        return isRetryable;
    }

    /**
     * Get the compact error message.
     *
     * @return compact error message
     */
    public String getMessage() {
        return shortMessage;
    }

    /**
     * Get concatenated message, a sequence of the (short) messages for the exception and the causes.
     *
     * @return concatenated messages for the exception and causes.
     */
    public String getConcatenatedMessage() {
        return concatenatedMessage;
    }

    /**
     * Get concatenated message, a sequence of the (short) messages for the exception and the causes.
     * <p/>
     * The response is usable inside double quotes.
     *
     * @return concatenated messages for the exception and causes.
     */
    public String getEscapedConcatenatedMessage() {
        return getConcatenatedMessage().replace('"', '\'').replace('\t', ' ').replace("\n", "\\n").replace("\r",
                "\\r");
    }

    /**
     * Get the stack trace as a string. Filters framework lines from the trace.
     *
     * @return stack trace
     */
    public String getStackTrace() {
        StringBuilder sb = new StringBuilder();
        Throwable exception = originalException;

        while (null != exception) {
            int filteredCount = 0;
            sb.append(exception.getMessage()).append('\n');
            for (StackTraceElement ste : exception.getStackTrace()) {
                if (shouldDisplay(ste.getClassName())) {
                    sb.append("    ");
                    while (filteredCount > 0) {
                        sb.append(',');
                        filteredCount--;
                    }
                    sb.append(ste.toString()).append('\n');
                } else {
                    filteredCount++;
                }
            }
            exception = exception.getCause();
        }
        return sb.toString();
    }

    private boolean shouldDisplay(String className) {
        for (String filter : FILTER_PREFIX) {
            if (className.startsWith(filter)) {
                return false;
            }
        }
        for (String filter : FILTER_CONTAINS) {
            if (className.contains(filter)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Extract a "sensible", reasonably compact message.
     *
     * @param exc exception to get message for
     * @return error message
     */
    private String getMessage(Throwable exc) {
        String msg;

        // see if there is a "getShortMessage" method
        Class clazz = exc.getClass();
        try {
            Method method = clazz.getMethod("getShortMessage");
            msg = (String) method.invoke(exc);
        } catch (Exception ex) {
            msg = null; /*ignore*/
        }

        // fallback to normal message or else class name
        if (msg == null || "null".equals(msg)) {
            msg = exc.getMessage();
        }
        if (msg == null || "null".equals(msg)) {
            msg = exc.toString();
        }

        return msg;
    }

    /**
     * Return the cause exception.
     *
     * @param exc exception to get message for
     * @return cause exception if any
     */
    private Throwable getCause(Throwable exc) {
        Throwable cause = exc.getCause();
        // use the roundabout way to figure out if there is a cause exception if none found easily
        // some classes, like EJBException need to be java 1.3 compatible and have a getCausedByException method
        if (cause == null) {
            Class clazz = exc.getClass();
            try {
                Method method = clazz.getMethod("getCausedByException");
                cause = (Throwable) method.invoke(exc);
            } catch (Exception ex) {
                cause = null; /*ignore*/
            }
        }
        return cause;
    }

}