javaloggingaopaspectjajdt

AOP - accessing a protected/private attribute from the intercepted class


I am working on a project that is basically a lot of processes that run periodically. Each process is a different class that extends an abstract class RunnableProcess we created, which contains a private attribute Map<String, String> result and the abstract method run with the signature below:

public abstract void run(Map processContext) throws IOException;

To improve modularization on the project, I'm starting to use Aspect Oriented Programming (AOP) to intercept the run calls from every RunnableProcess. I am still learning AOP, and I have the following code until now:

import static org.slf4j.LoggerFactory.getLogger;

import org.slf4j.Logger;
import process.RunnableProcess;
import java.util.Map;

public aspect ProcessRunInterceptor {
    private Logger logger;
    pointcut runProcess() : call(void RunnableProcess.run(Map));

    after(): runProcess() {
        logger = getLogger(thisJoinPoint.getClass());
        logger.info("process run successfully");
    }
}

It is working, but I want to log more information than just "process run successfully". I have this information inside the intercepted class, in the result attribute mentioned above. Is it possible to access it in the advice without changing the implementation of RunnableProcess?

I can (prefer not to, but if it would be the only choice...) change the attribute from private to protected, but I wouldn't change it to public. I also would not like to create a get method for it.


Solution

  • Building upon my answer to your other question, I will explain to you what you can do. A few hints:

    Here is an MCVE for you:

    Process classes:

    package de.scrum_master.app;
    
    import java.io.IOException;
    import java.util.Map;
    
    public abstract class RunnableProcess {
      protected String result = "foo";
    
      public abstract void run(Map processContext) throws IOException;
    }
    
    package de.scrum_master.app;
    
    import java.io.IOException;
    import java.util.Map;
    
    public class FirstRunnableProcess extends RunnableProcess {
      @Override
      public void run(Map processContext) throws IOException {
        System.out.println("I am #1");
        result = "first";
      }
    }
    
    package de.scrum_master.app;
    
    import java.io.IOException;
    import java.util.Map;
    
    public class SecondRunnableProcess extends RunnableProcess {
      @Override
      public void run(Map processContext) throws IOException {
        System.out.println("I am #2");
        result = "second";
      }
    }
    

    Driver application:

    package de.scrum_master.app;
    
    import java.io.IOException;
    
    public class Application {
      public static void main(String[] args) throws IOException {
        new FirstRunnableProcess().run(null);
        new SecondRunnableProcess().run(null);
      }
    }
    

    Aspect:

    Here you just bind the target() object to a parameter in the pointcut and use it in both advices.

    package de.scrum_master.aspect;
    
    import static org.slf4j.LoggerFactory.getLogger;
    
    import org.slf4j.Logger;
    import de.scrum_master.app.RunnableProcess;
    import java.util.Map;
    
    public privileged aspect ProcessRunInterceptorProtocol {
      pointcut runProcess(RunnableProcess process) :
        call(void RunnableProcess.run(Map)) && target(process);
    
      before(RunnableProcess process): runProcess(process) {
        Logger logger = getLogger(process.getClass());
        logger.info("logger = " + logger);
        logger.info("running process = " + thisJoinPoint);
      }
    
      after(RunnableProcess process): runProcess(process) {
        Logger logger = getLogger(process.getClass());
        logger.info("finished process = " + thisJoinPoint);
        logger.info("result = " + process.result);
      }
    }
    

    Console log with JDK logging (some noise removed):

    INFORMATION: logger = org.slf4j.impl.JDK14LoggerAdapter(de.scrum_master.app.FirstRunnableProcess)
    INFORMATION: running process = call(void de.scrum_master.app.FirstRunnableProcess.run(Map))
    I am #1
    INFORMATION: finished process = call(void de.scrum_master.app.FirstRunnableProcess.run(Map))
    INFORMATION: result = first
    INFORMATION: logger = org.slf4j.impl.JDK14LoggerAdapter(de.scrum_master.app.SecondRunnableProcess)
    INFORMATION: running process = call(void de.scrum_master.app.SecondRunnableProcess.run(Map))
    I am #2
    INFORMATION: finished process = call(void de.scrum_master.app.SecondRunnableProcess.run(Map))
    INFORMATION: result = second