10

I am trying to create a helper method that will eliminate the need of having code like this:

void foo() throws ExceptionA, ExceptionB, DefaultException {
  try {
     doSomething(); // that throws ExceptionA, ExceptionB or others
  } catch (Exception e) {
    if (e instanceof ExceptionA)
        throw new ExceptionA("extra message", e);
    if (e instanceof ExceptionB)
        throw new ExceptionB("extra message", e);

    throw new DefaultException("extra message", e);
  }
}

The problem is that I need to maintain the throws list in the function declaration and in the body of the function at the same time. I am looking how to avoid that and to make changing the throws list sufficient and my code to looks like:

void foo() throws ExceptionA, ExceptionB, DefaultException {
  try {
     doSomething(); // that throws ExceptionA, ExceptionB or others
  } catch (Exception e) {
    rethrow(DefaultException.class, "extra message", e);
  }
}

Where rethrow method will be smart enough to recognize the throws list from the method declaration.

This way when I change the list of type that my method propagates in the throws list I to not need to change the body.

The following is a function that could solve the problem. The problem is because it does not know what type of exception it will throw its throws declaration has to say Exception, but if it does this, the method that is going to use it will need to specify it as well, and the whole idea of using the throws list goes to hell.

Any suggestions how this could be solved?

@SuppressWarnings("unchecked")
public static void rethrow(Class<?> defaultException, String message, Exception e) throws Exception
{
  final StackTraceElement[] ste = Thread.currentThread().getStackTrace();

  final StackTraceElement element = ste[ste.length - 1 - 1];

  Method method = null;

  try {
     method = getMethod(element);
  } catch (ClassNotFoundException ignore) {
     // ignore the Class not found exception - just make sure the method is null
     method = null;
  }

  boolean preserveType = true;

  if (method != null) {

     // if we obtained the method successfully - preserve the type 
     // only if it is in the list of the thrown exceptions
     preserveType = false;

     final Class<?> exceptions[] = method.getExceptionTypes();

     for (Class<?> cls : exceptions) {
        if (cls.isInstance(e)) {
           preserveType = true;
           break;
        }
     }
  }

  if (preserveType)
  {
     // it is throws exception - preserve the type
     Constructor<Exception> constructor;
     Exception newEx = null;
     try {
        constructor = ((Constructor<Exception>) e.getClass().getConstructor());
        newEx = constructor.newInstance(message, e);
     } catch (Exception ignore) {
        // ignore this exception we prefer to throw the original
        newEx = null;
     }

     if (newEx != null)
        throw newEx;
  }

  // if we get here this means we do not want, or we cannot preserve the type
  // just rethrow it with the default type

  Constructor<Exception> constructor;
  Exception newEx = null;

  if (defaultException != null) {
     try {
        constructor = (Constructor<Exception>) defaultException.getConstructor();
        newEx = constructor.newInstance(message, e);
     } catch (Exception ignore) {
        // ignore this exception we prefer to throw the original
        newEx = null;
     }

     if (newEx != null)
        throw newEx;
  }

  // if we get here we were unable to construct the default exception
  // there lets log the message that we are going to lose and rethrow
  // the original exception

  log.warn("this message was not propagated as part of the exception: \"" + message + "\"");
  throw e;
}

Update 1: I can use RuntimeException to avoid the need of throws declaration, but in this case I am losing the type of the exception which is one of the most important points.

Ideas how I can resolve this?

gsf
  • 6,612
  • 7
  • 35
  • 64
  • Don't do this!!! You will lose the exception stack trace info, which is some of the most important info in the exception. Rather, throw a new exception (a single "MyFunctionFailedException" class for all cases) where the old exception is the "cause". – Hot Licks Jul 11 '14 at 00:17
  • 1
    I do not think I do, if I pass the previous exception as argument to the new one. – gsf Jul 11 '14 at 00:19
  • 1
    Just throw the "MyFunctionFailedException". – Hot Licks Jul 11 '14 at 00:19
  • What MyFunctionFailedException is? – gsf Jul 11 '14 at 00:38
  • 1
    This is completely unnecessary. If you can't handle an exception where you are, just let it bubble up until you can handle it. If you can handle it by throwing a completely different type of exception, throw that other type of exception. – jr. Jul 11 '14 at 00:43
  • 2
    the whole point is to preserve the type, but to extend the message with more details. I think this makes a lot of sense. There are several questions on this site how to do it. – gsf Jul 11 '14 at 00:46
  • MyFunctionFailedException is an exception you create. It indicates that your function had failed for the indicated reason. – Hot Licks Jul 11 '14 at 00:47
  • this is what the defaultException is, in case the type of the exception is not of interest, but if it is, I would like to preserve it. – gsf Jul 11 '14 at 00:48
  • this makes a lot of sense, and I am doing it where I can, but some times even a single method that I am calling may create the need of such type of handling – gsf Jul 11 '14 at 01:06
  • Won't the second approach in my answer work in that case? – jr. Jul 11 '14 at 01:26
  • It would not add the extra message for ExceptionA and ExceptionB. – gsf Jul 11 '14 at 01:34
  • There is no guarantee that `ExceptionA` and `ExceptionB` have constructors with the signature `public ExceptionX( String message, Throwable t )` - there isn't even any guarantee that `ExceptionA` and `ExceptionB` actually have public constructors at all. You may not be able to add an extra message to them. – jr. Jul 11 '14 at 01:51
  • If you want to add an extra message, you've handled the `Exception` in some way, and therefore it should probably be thrown as a `DefaultException`. – jr. Jul 11 '14 at 01:51
  • Yes, there will be cases that this is not going to be possible, in these cases even the current pattern is not going to work. With other words I will be able to replace it, because where it is used there is guarantee that there will not be the described problem. – gsf Jul 11 '14 at 02:00
  • If adding extra message means I "handled" the exception. I am not sure that I agree with this. The example does not make it clear, but i am using "extra message", because the idea is that the message is added to the previous ones. You could think about it as parallel to the call-stack trace, but from messages. – gsf Jul 11 '14 at 02:04
  • What helpful information is going to be contained in the extra message that isn't already contained in the trace / original message? – jr. Jul 11 '14 at 02:19
  • In most of the cases some of the values of the arguments that the method was called with in when the exceptional situation occurred. – gsf Jul 11 '14 at 03:50

2 Answers2

8

I'm guessing that code where you're doing real work (ie. the part where you're not tinkering with exceptions) looks like this.

public void doSomeWork( ... ) throws ExceptionA, ExceptionB, DefaultException
{
    try
    {
        // some code that could throw ExceptionA
        ...
        // some code that could throw OtherExceptionA
        ...
        // some code that could throw ExceptionB
        ...
        // some code that could throw OtherExceptionB
    }
    catch (Exception e) 
    {
        if( e instanceof ExceptionA )
        {
            throw new ExceptionA("extra message", e);
        }
        if( e instanceof ExceptionB )
        {
            throw new ExceptionB("extra message", e);
        }

        throw new DefaultException("extra message", e);
     }
}

There are two better approaches

First Approach

public void doSomeWork( ... ) throws ExceptionA, ExceptionB, DefaultException
{
    // some code that could throw ExceptionA
    ...
    try
    {
        // some code that could throw OtherExceptionA
        ...
    }
    catch (Exception e) 
    {
        throw new DefaultException("extra message", e);
    }
    // some code that could throw ExceptionB
    ...
    try
    {
        // some code that could throw OtherExceptionB
    }
    catch (Exception e) 
    {
        throw new DefaultException("extra message", e);
    }
}

Second Approach

public void doSomeWork( ... ) throws ExceptionA, ExceptionB, DefaultException
{
    try
    {
        // some code that could throw ExceptionA
        ...
        // some code that could throw OtherExceptionA
        ...
        // some code that could throw ExceptionB
        ...
        // some code that could throw OtherExceptionB
    }
    catch (OtherExceptionA | OtherExceptionB e) 
    {
        throw new DefaultException("extra message", e);
    }
}

The first approach is good if you want to continue execution at all costs and catch and wrap RuntimeExceptions if you run into them. Generally you don't want to do this, and it's better to let them propagate up, as you probably can't handle them.

The second approach is generally the best. Here you're explicitly pointing out which exceptions you can handle, and dealing with them by wrapping them. Unexpected RuntimeExceptions propagate up, as they should unless you have some way of dealing with them.

Just a general comment: playing with StackTraceElements isn't considered to be a great idea. You may end up getting an empty array from Thread.currentThread().getStackTrace() (although you most likely will not if using a modern Oracle JVM), and the depth of the calling method isn't always length-2, it may be length-1 particularly in older versions of the Oracle JVM.

You can read more about this problem in this question.

Community
  • 1
  • 1
jr.
  • 1,699
  • 14
  • 31
  • Downvoting without leaving a comment helps no-one. – jr. Jul 11 '14 at 00:23
  • It wasn't me to downvote it, but how the usage of this is going to look if I have foo() throws ExceptionA, ExceptionB for example? – gsf Jul 11 '14 at 00:29
  • The problem is that the suggested generic declaration does not eliminate the need of the caller method to add to its throws declaration the specified extends Type. – gsf Jul 11 '14 at 00:54
0

To elaborate on what )some) people are telling you, this is MyFunctionFailedException, ofcourse it should be named something more sensible:

public class MyFunctionFailedException extends Exception {
    public MyFunctionFailedException(String message, Throwable cause) {
        super(message, cause);
    }
}

Then your catch block becomes something like this.

try {
...
} catch (Exception e) {
    throw new MyFunctionFailedException("extra message", e);
}

If you really want to rethrow a lower level exception, you should use multiple catch blocks. Be aware tho' that not all types of Exceptions necessarily has a constructor that let's you add a cause. And you really should think about why it makes sense for your method to let for instance an uncaught SQLException bubble up the call stack.

Mikkel Løkke
  • 3,710
  • 23
  • 37
  • Thank you, but it seems like I should know about this if I am using it in my example what problem I am trying to solve ... the line with `throw new DefaultException("extra message", e);` – gsf Jul 11 '14 at 00:59
  • 1
    Sure. The thing is that the problem you're trying to solve, is one of your own creation spawned by poor design (in regards to placing responsibility). It is almost certain that whichever code uses the method in your example doesn't actually care whether the cause of the exception is an `SQLException`, a `SocketTimeoutException` or an `IOException`, because there's nothing it can do about it. It would care about a `DataAccessException`/`DefaultException`/`MyFunctionFailedException`, because it can do something sensible with that. – Mikkel Løkke Jul 11 '14 at 01:19
  • I am not sure that what you saying even perfectly correct in its second part is in anyway connected with the conclusion from your first part of the comment. In every method you have to make the decision which exceptions to handle, and which to propagate. This is not a problem of my own creation. The problem I am trying to solve is to have a single place in the code where I to express my decision - instead of two when I would like to add an extra message. – gsf Jul 11 '14 at 01:30