39

I need to check if an exception is caused by some database problem. I receive an Exception and check if its cause contains the "ORA" string and return that (something like "ORA-00001"). The problem here is that the exception I receive is nested inside other exceptions, so if I don't find out if it's an oracle exception, I have to check into the cause of that exception and so on. Is there a cleaner way to do this? Is there a way to know the first cause (the deep-nested exception) of a given exception?

My current code looks like this:

private String getErrorOracle(Throwable e){
        final String ORACLE = "ORA";
        if (e.getCause() != null && e.getCause().toString().contains(ORACLE)){
            return e.getCause().toString();
        } else if(e.getCause() != null){
            return getErrorOracle(e.getCause());
        } else {
            return null;
        }
    }
Andrea Dusza
  • 2,080
  • 3
  • 18
  • 28
Averroes
  • 4,168
  • 6
  • 50
  • 63

12 Answers12

90

In the interests of not reinventing the wheel, if you're using Apache Commons Lang, then look at ExceptionUtils.getRootCause().

Is it worth including a library just for that? Maybe not. But if you already have it on your classpath, it's there for you, and note that it does some things that a 'naive' implementation might not do (e.g. deal with cycles in the cause chain... ugh!)

adelarsq
  • 3,718
  • 4
  • 37
  • 47
Cowan
  • 37,227
  • 11
  • 66
  • 65
20

If you are already on Guava than Throwables.getRootCause() comes to the rescue.

xlm
  • 6,854
  • 14
  • 53
  • 55
Nándor Krácser
  • 1,128
  • 8
  • 13
16

Just traverse the exception chain until you get to an exception with no cause, and then just return that message, if you want the last one.

Your function will only get the first cause, if there is one.

You may want to look at finding the first cause in your package though, as the actual deepest one may be an oracle exception, which is helpful, but unless you can see where you created the problem, you will have a hard time fixing it.

James Black
  • 41,583
  • 10
  • 86
  • 166
  • 1
    Thanks. I think I'll go this way then. I have to do this because the cliente want to show some specific messages when the exception is caused by any problem related to the data base. When I receive the exception I don't know if it was caused by the data base since the first exception is nested inside the exception chain so I was looking for a way to solve this. – Averroes Nov 24 '09 at 17:52
  • 1
    ehm, his method is recursive, hence he will get the root cause. – Bozho Nov 24 '09 at 18:35
  • @Bozho - He has three if statements with returns. If he had it call itself again then it would be recursive, but, he would need to change his logic, so it will exit. – James Black Nov 24 '09 at 19:19
  • The second if statement does call itself and the logic looks right, I don't see why this would not find the right exception. – vickirk Nov 25 '09 at 09:01
4

Probably a bit overkill for your usage but I think it is cleaner (and reusable)

interface ThrowablePredicate {
    boolean accept(Throwable t);
}

public OracleErrorThrowablePredicate implements ThrowablePredicate {
    private static final ORA_ERR = "ORA";

    public boolean accept(Throwable t) {
        return t.toString().contains(ORA_ERR);
    }
}


public class CauseFinder {

   private ThrowablePredicate predicate;

   public CauseFinder(ThrowablePredicate predicate) {
      this.predicate = predicate;
   }

   Throwable findCause(Throwable t) {
      Throwable cause = t.getCause();

      return cause == null ? null 
         : predicate.accept(cause) ? cause : findCause(cause)
   }
}


// Your method
private String getErrorOracle(Throwable e){
    return new CauseFinder(new OracleErrorThrowablePredicate()).findCause(e);
}
Jason Plank
  • 2,336
  • 5
  • 31
  • 40
vickirk
  • 3,979
  • 2
  • 22
  • 37
2

I think that any error that is thrown by oracle will be wrapped in a SQLException (somebody please correct me if wrong). Once you have accessed the SQLException you should be able to call

getErrorCode() Retrieves the vendor-specific exception code for this SQLException object.

Let me know if this works as I have never tried it :-)

Karl

Karl
  • 2,927
  • 8
  • 31
  • 39
  • Yes. It seems that the exception thrown by Oracle extends SQLException. Regarding getErrorCode() this seems to return a number and I guess you're right about this. I'll check it tomorrow at work. Thanks. – Averroes Nov 24 '09 at 18:53
2

You could improve your code checking for SQLException

import java.sql.SQLException;

private static final String ORACLE = "ORA";

public String doHandle(Throwable t) {
    if (t.getClass().isAssignableFrom(SQLException.class)) {
    SQLException e = (SQLException) t;
    int errCode = e.getErrorCode();
    String state = e.getSQLState();
    String msg = e.getMessage();
    if (msg.contains(ORACLE)) {
        return msg;
        }
    } else {
        if (t.getCause() != null) {
            return this.doHandle(t.getCause());
            }
        }
    return "";
}

Also, I think in Oracle "errCode" contains the number associated to ORA-nnnn

JuanZe
  • 8,007
  • 44
  • 58
  • 2
    Thanks. This help me a lot to improve the function. I just changed "t.getClass().isAssignableFrom(SQLException.class)" to "t instaceof SQLException" because the first one wasn't being evaluated true (The exception I was testing were BatchUpdateException) – Averroes Nov 24 '09 at 18:56
2

In my Spring Boot project for getRootCause IDEA suggest 3 static import:

Spring Core: org.springframework.core.NestedExceptionUtils.getRootCause
Jackson: com.fasterxml.jackson.databind.util.ClassUtil.getRootCause
Guava (Swagger transitive) com.google.common.base.Throwables.getRootCause

Most smart (with cycle check) is Spring NestedExceptionUtils.getRootCause. But, if your excetion has no cause, method returns null. In my case it is wrong, so I've done:

@NonNull
public static Throwable getRootCause(@NonNull Throwable t) {
    Throwable rootCause = NestedExceptionUtils.getRootCause(t);
    return rootCause != null ? rootCause : t;
}
Grigory Kislin
  • 16,647
  • 10
  • 125
  • 197
1

ExceptionUtils.getRootCause(), and Throwables.getRootCause() returns null if the cause of the exception being passed is null.The following method will return the original throwable if an throwable without a cause is being passed as the input parameter.

/**
 * @return the root cause of a given throwable.
 *   If throwable without a cause is being passed, the original throwable will be returned
 */
public static Throwable getRootCause(@NonNull final Throwable throwable) {
    List<Throwable> throwableList = ExceptionUtils.getThrowableList(throwable);
    return throwableList.get(throwableList.size() - 1);
}
Gayan Weerakutti
  • 11,904
  • 2
  • 71
  • 68
0

One line solution using core Java API:

    try {
        i = 1 / 0; 
    } catch (ArithmeticException e) {
        System.out.println(new ArithmeticException().initCause(e).getCause());
    }

One more solution below works as well:

    try {
        i = 1 / 0; 
    } catch (ArithmeticException e) {
        System.out.println(new Exception().initCause(e).getCause());
    }

Both of them will print

java.lang.ArithmeticException: / by zero

Anatolii Shuba
  • 4,614
  • 1
  • 16
  • 17
0

I want to add Kotlin extension functions to get root causes:

fun Throwable.rootCause(): Throwable {
  return if (cause == null) this else cause!!.rootCause()
}

//Return null if first cause is null
fun Throwable.optRootCause(): Throwable? {
  return if (cause == null) null else cause!!.rootCause()
}

Or this one if need to find a cause inside the throwable chain at any points:

fun <T : Throwable> Throwable.isOrContainsCauseOfType(clazz: KClass<T>): Throwable?  {
  return when {
    clazz.isInstance(this) -> this //remove if you want to exclude [this]
    cause == null -> null
    clazz.isInstance(cause) -> cause
    else -> cause!!.isOrContainsCauseOfType(clazz)
  }
}
MatPag
  • 41,742
  • 14
  • 105
  • 114
-1

If the exception being thrown is always going to be of a specific type, like OracleException, you can catch just that exception.

For example:

try {

    ...

} catch(OracleException oe) {

    ...

}

This would only apply if there are specific Oracle exceptions being thrown. I don't know much about Oracle, so before attempting this you will probably want to find out if that's what's happening.

Undo
  • 25,519
  • 37
  • 106
  • 129
spork
  • 1,185
  • 1
  • 11
  • 17
-2

on 28-01-2015 , i have unable to solve my problem with any of the above solution, so my recommendation is to use :

e.getMessage().toString();

Ps: i am using it on android.

Fasiha
  • 488
  • 3
  • 11
  • .toString() is redundant besides the fact that this does not do what was requested. – pqsk Feb 17 '17 at 20:43