Search code examples
javaspring-mvctimeraspectjaspect

implement aspectj logging inside java timer


I have a method that throws IOException which is called from a java timer run().Since the run() doesn't return any checked exceptions and I am using AspectJ for logging, how do I log the exception in aspectj?

This is my code:

timer = new Timer(); `

    timer.scheduleAtFixedRate(new TimerTask() {

        public void run() {

            for (OidTypes oidType : typesList) {

                try {
                    valueIncrementOperation(oidType);//------this method throws exception
                } catch (IOException e) {

                    System.out.println("Error occured inside Timer : "
                            + e.getMessage());

                    timer.cancel();
                    timer = null;
                }

            }

` I am forced to use try/catch for now. What are my options?


Solution

  • First you need to pack the IOException into an unchecked exception, the real exception will be the cause of the exception you catch in the advice.

    The simplest is to take RuntimeException.

    public void doTimerJob() {
        final Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    throw new IOException("file not found");
                } catch (IOException e) {
                    timer.cancel();
                    throw new RuntimeException(e);
                }
            }
        }, new Date(), 2000);
    }
    

    Then you could try the following:

    Create a point cut for TimerTask#run

    pointcut timerTaskRun() : execution(public * java.util.TimerTask.run(..) );
    

    and an advice for after throwing a RuntimeException

    after() throwing(RuntimeExceptione) : timerTaskRun() {
        System.out.println("log and rethrow " + e.getCause().getMessage());
    }
    

    This will re-throw the exception after logging it.

    If you want to log and swallow the exception you could write an around advice

    Object around() : timerTaskRun() {
        Object o;
        try {
            o = proceed();   
        } catch(RuntimeException e) {
            System.out.println("log and swallow " + e.getCause().getMessage());
            o = null;
        }
        return o;
    }
    

    Note that you should only have only one of the advices, either after throwing or around not both.

    But you may not want to advice all TimerTask#run calls along with all RuntimeExceptions. In this case you should create own types which you should use in the point cut and the advices.

    "Unchecked" IOException

    public class IOExceptionUnchecked extends RuntimeException {
        private static final long serialVersionUID = 1L;
        public IOExceptionUnchecked(IOException e) {
            super(e);
        }
    }
    

    Custom TimerTask

    public class MyTimerTask extends TimerTask  {
        Timer owner = null; 
        public MyTimerTask(Timer timer) {this.owner = timer;}
        @Override
        public void run() {
            try {
                throw new IOException("file not found");
            } catch (IOException e) {
                owner.cancel();
                throw new IOExceptionUnchecked(e);
            }
        }
    }
    

    Point cut

    pointcut timerTaskRun() : execution(public * com.example.MyTimerTask.run(..) );
    

    after throwing advice:

    after() throwing(IOExceptionUnchecked e) : timerTaskRun() {
        System.out.println("log and rethrow " + e.getCause().getMessage());
    }
    

    Or around advice

    Object around() : timerTaskRun() {
        Object o;
        try {
            o = proceed();   
        } catch(IOExceptionUnchecked e) {
            System.out.println("log and swallow " + e.getCause().getMessage());
            o = null;
        }
        return o;
    }