1

I've developed a nice Spring Aspect which I can use to monitor my service operations performance. If some operations are taking a long time to execute, it logs them.

@Aspect
public class PerformanceMonitorAspect {

    private Logger logger = LoggerFactory.getLogger("performance");

    @Pointcut("execution(* com.company.MyService+.*(..))")
    public void pointCut(){

    }

    @Around("pointCut()")
    public Object profileServiceMethods(ProceedingJoinPoint thisJoinPoint) throws Throwable {
        MethodSignature ms = (MethodSignature) thisJoinPoint.getSignature();
        Method m = ms.getMethod();
        long t1 = System.nanoTime();
        Object result = thisJoinPoint.proceed();
        long t2 = System.nanoTime();
        long millis = TimeUnit.NANOSECONDS.toMillis(t2 - t1);
        if (millis < 1000) {
            logger.trace("Execution time for {}: {} ms", m.getName(), millis);
        } else {
            logger.warn("Substantial execution time for {}: {} ms", m.getName(),
                    millis);
        }
        return result;
    }

}

However, as it's an @Around Advice, Spring takes control of all my method calls in order to profile them. That's a bit uncomfortable while debugging (it even misleads Eclipse itself), so I would like the Pointcut to be executed only when my application is at production stage. Can I configure the Pointcut to be conditionally executed depending on a an enviroment variable, Java property or similars?

Documentation only refers to method variable conditions... Thanks in advance!

EDIT

As of @DavidL's suggestion, I changed my pointcut to this:

@Pointcut("execution(* com.tadic.module.TadicModuleGeneric+.*(..)) && if()")
public static boolean pointCut() {
    return true;
}

That's the way Eclipse doesn't warn about anything. However, I get this at runtime:

GRAVE: Critical error during deployment: 
java.lang.VerifyError: Expecting a stackmap frame at branch target 7
Exception Details:
  Location:
    com/mycompany/aspects/AuditAspect.<clinit>()V @1: invokestatic
  Reason:
    Expected stackmap frame at this location.
  Bytecode:
    0000000: 00b8 0134 a700 084b 2ab3 012f b1       
  Exception Handler Table:
    bci [1, 7] => handler: 7

    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2615)
    at java.lang.Class.getDeclaredMethods(Class.java:1860)
    at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:474)
    at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:458)
    at org.springframework.util.ReflectionUtils.getUniqueDeclaredMethods(ReflectionUtils.java:518)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:639)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:575)
    at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1350)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:355)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:326)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:434)
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:624)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:461)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:410)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4973)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5467)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

So it seems the weaver isn't doing it properly. Any suggestions?

EDIT 2 This issue happens when I use AspectJ via Spring AOP proxy-based aspects. I'm using Maven to build the project and Eclipse AspectJ plugin to integrate it in my workspace. AspectJ version is 1.8.2, Spring-AOP version is 3.2.8.RELEASE and I'm building it with the Java 7.0.75 JDK.

Here it is a sample of the POM being used:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.tesicnor.test</groupId>
    <artifactId>aspect-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>aspect-test</name>
    <url>http://maven.apache.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
        <aspectj.version>1.8.2</aspectj.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.7</version>
                <configuration>
                    <complianceLevel>1.7</complianceLevel>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>3.2.8.RELEASE</version>
        </dependency>
    </dependencies>


</project>

This POM is the one for a functional testcase I created. My current project is huge and seems that there's other dependency causing the trouble.

Aritz
  • 30,971
  • 16
  • 136
  • 217
  • There used to be a few bugs causing stack traces like yours in early versions of AspectJ 1.8 with regard to Java 8. Can you please tell me which exact Java and AspectJ versions you are using? How do you build? Maven? If so, show the POM. Are you using proxy-based Spring AOP or native AspectJ via LTW or CTW? Please provide more info so as to make the problem reproduceable. – kriegaex Mar 14 '15 at 13:27
  • Hello @kriegaex. Many thanks for your interest, first of all. Actually, if I create a new workspace in Eclipse and build a test case from scratch, it works. So it seems something is polluting my classpath. I'm going to update the question with the requested info. – Aritz Mar 16 '15 at 07:37
  • 1
    What I do not understand is why you would need *AspectJ Maven Plugin* for Spring AOP. You only need it for AspectJ, not for Spring AOP, because the latter does not compile anything but uses dynamic proxies. Maybe you are trying to apply the same aspects in two ways at once. Very strange indeed. And BTW, *aspectjweaver.jar* is only needed for AspectJ load-time weaving, not for compile.time weaving. So it is redundant in this case anyway, even if you do use AspectJ. – kriegaex Mar 16 '15 at 19:35
  • @kriegaex, thanks for your advices. I'm new in aspects, so I really need to understand them deeper. I'll try to fix my project and remove unnecessary dependencies - plugins. – Aritz Mar 16 '15 at 19:43
  • If you are ready to provide a reproduceable showcase in the form of an [SSCCE](http://sscce.org/), ideally as a Maven project on GitHub, I would try to take a look and provide some more concrete counsel. – kriegaex Mar 16 '15 at 19:48
  • @kriegaex, I think the root of my problems comes from I want an Spring bean to be injected in the Aspect. Some answers encourage to weave it using the AspectJ and later on tell Spring this Aspect has been built by it: http://stackoverflow.com/a/11445798/1199132 – Aritz Mar 17 '15 at 16:40

2 Answers2

1

You can get a reference to the attribute (probably in a static way) which represent your current stage. Depending on it value, you can bypass the code inside the profileServiceMethods method (early return).

In a less esthetically way, you can declare a boolean var in your aspect for the same purpose:

if(!logActivated)
    return null;
else
    <your code here>

EDIT:

Just take a look at the doc. Maybe you can do it this way:

@Pointcut("execution(* com.company.MyService+.*(..)) && args(i) && if()")
public static boolean pointCut(int i) {
    return i == State.PRODUCTION_STAGE ;
}
DavidL
  • 1,120
  • 1
  • 15
  • 34
  • I've just edited my post, including an attempt with your piece of code. It seems to be a good idea, but can't get it work :-( – Aritz Mar 13 '15 at 14:19
  • Looks like it may be a compliance level issue. Check [here](http://aspectj.2085585.n4.nabble.com/Stackmap-frame-errors-when-building-the-aspectj-project-with-Java-1-7-td4651609.html) and [here](http://heshans.blogspot.ca/2014/10/stackmap-frame-errors-when-building.html) for more info – DavidL Mar 13 '15 at 14:34
0

Finally, I got it working. It seems Eclipse is doing some mess in my classpath, causing the problem above. I removed the aspectjweaver and I can get work the following code, based in @DavidL's answer:

@Aspect
public class PerformanceMonitorAspect {

    /**
     * Decide whether the Pointcut to be executed or not
     */
    private static boolean enabled;

    @Pointcut("execution(* com.company.MyService+.*(..)) && if()")
    public static boolean pointCut() {
        return enabled;
    }

    private Logger logger = LoggerFactory.getLogger("performance");

    @Around("pointCut()")
    public Object profileServiceMethods(ProceedingJoinPoint thisJoinPoint) throws Throwable {
        MethodSignature ms = (MethodSignature) thisJoinPoint.getSignature();
        Method m = ms.getMethod();
        long t1 = System.nanoTime();
        Object result = thisJoinPoint.proceed();
        long t2 = System.nanoTime();
        long millis = TimeUnit.NANOSECONDS.toMillis(t2 - t1);
        if (millis < 1000) {
            logger.trace("Execution time for {}: {} ms", m.getName(), millis);
        } else {
            logger.warn("Substantial execution time for {}: {} ms", m.getName(),
                    millis);
        }
        return result;
    }

    //The value is retrieved by Spring having read a config file written by Maven, depending on the profile
    @Value("${enable.performance.monitor}")
    public void setEnabled(boolean value) {
        enabled = value;
    }

}

Then, let Spring manage the aspect:

<bean class="com.tadic.aspects.PerformanceMonitorAspect" factory-method="aspectOf" />
Aritz
  • 30,971
  • 16
  • 136
  • 217
  • Can it happen that the method targeted by pointcut gets called/executed by Spring(or other spring beans) before Spring injects `enable.performance.monitor` property? – sjakovac Jul 22 '22 at 08:25