2

My idea is to use AspectJ to catch exceptions in annotated methods and if any exceptions are thrown, the annotated method should try to run again. I have mostly followed this tutorial (http://zoftware.blogspot.cz/2008/02/using-aspectj-and-java-annotations-to_23.html) but I can't get it to work. Everything should be ok, yet it isn't. Exceptions are caught before finally and there are many exceptions thrown, not just one. Catch inside my aspect seems not to work at all. I use AspectJ 1.7.3. The code is...

Annotation:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryIfFailed {

    int maxRetries() default 5;
}

Annotated method:

@RetryIfFailed(maxRetries = 3)
private User showUser(String userName) {
    try {
        return twitter.showUser(userName);
    } catch (TwitterException e) {
        System.out.println("I am catch inside showUser");
    }
    return null;
}

Aspect:

    @Around("call(@RetryIfFailed * *..*(..))")
    public Object retryMaxRetriesTimes(ProceedingJoinPoint thisJoinPoint) throws Throwable {
        System.out.println("Entering retryMax...");
        Method method = ((MethodSignature) thisJoinPoint.getSignature()).getMethod();
        RetryIfFailed annotation = method.getAnnotation(RetryIfFailed.class);
        int retries = annotation.maxRetries();

        Object ret = null;
        while (retries > 0) {
            try {
                System.out.println("Before proceeding... Retries=" + retries);
                ret = thisJoinPoint.proceed();
            } catch (Throwable e) {
                System.out.println("I am catched in RetryMax ");
                retries--;
                if (retries == 0) {
                    System.out.println("Exception caught. Rethrowing..." + e);
                    throw new ConnectionErrorException("Twitter service failed to establish connection", e);
                }
            } finally {
                System.out.println("Finally block..." + retries);
                if (ret != null) {
                    System.out.println("Object returned: " + ret);
                    return ret;
                }

                System.out.println("Decresing retries to" + retries);
                retries--;
                if (retries == 0) {
                    throw new ConnectionErrorException("It should not get here.");
                }
            }
        }

        //should never be reached
        return null;
    }
}

Maven configuration:

<!-- Build with AspectJ-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.5</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <complianceLevel>1.7</complianceLevel>
                    <verbose>true</verbose>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                </dependencies>
            </plugin>
        </plugins>
    </build>

Output:

Entering retryMax...
Before proceeding... Retries=3
I am catch inside showUser
Finally block...3
Decresing retries to3
Before proceeding... Retries=2
I am catch inside showUser
Finally block...2
Decresing retries to2
Before proceeding... Retries=1
I am catch inside showUser
Finally block...1
Decresing retries to1
Exception in thread "main" ...<path>....ConnectionErrorException: It should not get here.
       at ...<stackTrace follows>...

Thanks for any advice :).

EDIT

As mvieghofer suggested, I never rethrow the exception. I expected @Around to catch exception inside twitter.showUser() and that was wrong. In case anybody would be interested in solution, here it is:

    @RetryIfFailed
    public static User showUser(String userName) throws ConnectionErrorException {
        try {
            return twitter.showUser(userName);
        } catch (TwitterException e) {
            throw new ConnectionErrorException(exceptionMessage, e);
        }
    }
Samuel
  • 2,430
  • 5
  • 31
  • 41
  • 1
    code looks like its running as it should be. you never throw a twitter exception. you should use afterthrowing, and rethrow when retries is 0. – aepurniet Nov 14 '13 at 15:30

1 Answers1

2

There is an after throw Exception advice for AspectJ.

You could have something like this:

aspect A {
  pointcut publicCall(): call(@RetryIfFailed * *..*(..));
  after() throwing (TwitterExepction e): publicCall() {
  System.out.println("Threw an exception: " + e);
  }

}

Also you should rethrow the TwitterException inside your showUser Method. For more infos on the after() throwing advice see this link

mvieghofer
  • 2,846
  • 4
  • 22
  • 51
  • Yes but that is not the problem. I throw ConnectionErrorException but in my annotated method TwitterException is thrown. And it is thrown 3 times - that basically means that "ret = thisJoinPoint.proceed();" is indeed executed 3 times but the exception is not caught inside the aspect ("catch (Throwable e)") – Samuel Nov 14 '13 at 12:35
  • No, but thanks for trying :). Retries are ok, last cycle of while would not happen with retries == -1. I purposely throw different exception than TwitterException to see how it behaves. I will probably change it in the production code. I've looked at AfterThrowing before - but it will still throw an exception, right? Is there a way to catch an exception, discard it and run the method again? – Samuel Nov 14 '13 at 12:49
  • 1
    Actually when an exception is thrown and then catched but not rethrown it will not appear somewhere else. If you would want such behavior the exception needs to be rethrown. I don't know if this is true for aspects, though. Nevertheless, whenn a TwitterException is thrown inside the `twitter.showUser(userName);` it is catched by your showUser method and will never reach any other method (since you do not rethrow it). – mvieghofer Nov 14 '13 at 12:56
  • Yes I understand how exceptions work but is it possible to throw an exception in twitter.showUser, catch it in aspectJ and discard it so it won't be caught by my method showUser? – Samuel Nov 14 '13 at 13:05
  • I've looked more into it and actually you were right. I made a logical mistake. I was expecting to catch my exception *inside* twitter.showUser but instead I was catching it inside my own showUser. Since you've pointed me in the right direction, I'll mark you as a correct answer, just delete the part about retries please, it is irrelevant to the question :). Thank you very much! :) – Samuel Nov 14 '13 at 17:40