Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java annotation for wrapping a method

I have a lot of boilerplate code that basically follows this pattern:

function doSomething() {
  try {
    [implementation]
    [implementation]
    [implementation]
    [implementation]
  } catch (Exception e) {
    MyEnv.getLogger().log(e);
  } finally {
    genericCleanUpMethod();
  }
}

I'd love to create my own annotation to clean my code up a bit:

@TryCatchWithLoggingAndCleanUp
function doSomething() {
  [implementation]
  [implementation]
  [implementation]
  [implementation]
}

The method signatures vary wildly (depending on the actual implementation of the method), but the boilerplate try/catch/finally part is always the same.

The annotation I have in mind would automatically wrap the contents of the annotated method with the whole try...catch...finally hoopla.

I've searched high and low for a straightforward way to do this, but have found nothing. I don't know, maybe I just can't see the woods for all the annotated trees.

Any pointers on how I might implement such an annotation would be greatly appreciated.

like image 952
TroutKing Avatar asked Dec 28 '11 15:12

TroutKing


People also ask

How do you use wrap method in Java?

wrap(int[] array, int offset, int length) The wrap() method wraps an int array into a buffer. The new buffer will be backed by the given int array; that is, modifications to the buffer will cause the array to be modified and vice versa. The new buffer's capacity will be array.

Can try catch block be annotated?

2. Re: try catch around annotated method. Right approach, but Seam doesn't support method level interceptors, only class level interceptors. So you can attach the interceptor at class level, and then mark the method with the same annotation, and only intercept those.


3 Answers

To do this, you would need some AOP framework that would use a proxy around your method. This proxy would catch the exception and execute the finally block. Quite frankly, if you don't use a framework supporting AOP already, I'm not sure I would use one just to save these few lines od code.

You could use the following pattern to do this in a more elegant way, though:

public void doSomething() {
    logAndCleanup(new Callable<Void>() {
        public Void call() throws Exception {
            implementationOfDoSomething();
            return null;
        }
    });
}

private void logAndCleanup(Callable<Void> callable) {
    try {
        callable.call();
    } 
    catch (Exception e) {
        MyEnv.getLogger().log(e);
    } 
    finally {
        genericCleanUpMethod();
    }
}

I just used Callable<Void> as an interface, but you could define your own Command interface:

public interface Command {
    public void execute() throws Exception;
}

and thus avoid the need to use a generic Callable<Void> and return null from the Callable.

EDIT: in case you want to return something from your methods, then make the logAndCleanup() method generic. Here's a complete example:

public class ExceptionHandling {
    public String doSomething(final boolean throwException) {
        return logAndCleanup(new Callable<String>() {
            public String call() throws Exception {
                if (throwException) {
                    throw new Exception("you asked for it");
                }
                return "hello";
            }
        });
    }

    public Integer doSomethingElse() {
        return logAndCleanup(new Callable<Integer>() {
            public Integer call() throws Exception {
                return 42;
            }
        });
    }

    private <T> T logAndCleanup(Callable<T> callable) {
        try {
            return callable.call();
        }
        catch (Exception e) {
            System.out.println("An exception has been thrown: " + e);
            throw new RuntimeException(e); // or return null, or whatever you want
        }
        finally {
            System.out.println("doing some cleanup...");
        }
    }

    public static void main(String[] args) {
        ExceptionHandling eh = new ExceptionHandling();

        System.out.println(eh.doSomething(false));
        System.out.println(eh.doSomethingElse());
        System.out.println(eh.doSomething(true));
    }
}

EDIT : And with Java 8, the wrapped code can be a bit prettier :

public String doSomething(final boolean throwException) {
    return logAndCleanup(() -> {                
        if (throwException) {
            throw new Exception("you asked for it");
        }
        return "hello";                
    });
}
like image 187
JB Nizet Avatar answered Oct 11 '22 23:10

JB Nizet


You could use dynamic proxies to implement this. It takes a bit of setting up, but once done, is pretty straightforward.

First, you define an interface and place the annotation on the interface.

public interface MyInterface {
    @TryCatchWithLogging
    public void doSomething();
}

Now, when you want to provide an implementation of the interface to a consumer, dont provide with him with the actual implementation, but instead a Proxy to it.

MyInterface impl = new java.lang.reflect.Proxy.newProxyInstance(
                         Impl.class.getClassLoader(), 
                         Impl.class.getInterfaces(), YourProxy(new Impl());

Then implement YourProxy.

public class YourProxy implements InvocationHandler {
....

     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
         if ( method.isAnnotationPresent(TryCatchLogging.class) ) {
              // Enclose with try catch
}
like image 32
Kal Avatar answered Oct 11 '22 22:10

Kal


you can implement annotation and annotation processor yourself and instrument code everytime when you do compilation (javac -processor). Other way is to use AOP, say AspectJ or Spring AOP (If you use Spring).

like image 3
korifey Avatar answered Oct 12 '22 00:10

korifey