Search code examples
javagradleaspectjgradle-pluginaspectj-maven-plugin

Gradle + AspectJ log tracing is trying to recompile the project during “instrumentation” and failing


Edit:

I've pushed dummy source code here https://github.com/swapnil-kotwal-sp/basic-demo

It seems to be compiling as well as doing AspectJ instrumentation as well?

But, I'm getting warning during instrumentation as

[ant:iajc] [warning] build config error: skipping missing, empty or
 corrupt aspectpath entry: 
/Users/swapnil.kotwal/Downloads/basic-demo/my-demo/src/com/my/demo/TracingAspect.java

Can someone help me with getting this TracingAspect.java issues fixed?

My ant target look like as below.. which I've converted into Gradle build script as shown in next code block

<target name="weave-aspects" depends="compile, jar-common" unless="weave.suppressed"
          description="weaves the AspectJ's aspects into classes compiled by compile target">
    <taskdef resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties">
      <classpath>
        <pathelement location="${abcd.proj.base}/lib/aspectj-1.9.4/aspectjtools-1.9.4.jar" />
      </classpath>
    </taskdef>

    <echo message="Weaving the AspectJ's aspects into class files ..." />

    <iajc inpath="${classes}" destdir="${classes}"
          debug="true" fork="true"
          maxmem="${iajc.memoryLimit}"
          source="${javaVersion}">
    <!-- path of jar containing TracingAspect -->
    <aspectpath>
       <path>
         <fileset dir="${pqr.bundle.image}" includes="pqr-bundle-common*.jar" />
       </path>
    </aspectpath>
    <classpath>
       <pathelement path="${deps}" />
       <pathelement path="${abcd.proj.base}/lib/aspectj-1.9.4/aspectjrt-1.9.4.jar" />
      </classpath>
    </iajc>
  </target>

Gradle conversion is as


plugins {
        id "io.freefair.aspectj.post-compile-weaving" version "5.1.1"
        //id "aspectj.gradle" version "0.1.6"
        //id "aspectj.AspectjGradlePlugin" version "0.0.3"
    }

configurations {
    ajc
    aspects
    compile {
        extendsFrom aspects
    }
}

ext {
    aspectjVersion = '1.9.2'
}

dependencies {
    implementation gradleApi() // for custom gradle task/pugin development

    compile project('my-project')
    compile project('my-project1')
    compile project('my-project2')
    // ...

    ajc "org.aspectj:aspectjtools:$aspectjVersion"
    compile "org.aspectj:aspectjrt:$aspectjVersion"
    compile "org.aspectj:aspectjweaver:$aspectjVersion"
    aspects project('my-project1')
    // ...
}


compileJava {
    dependsOn configurations.ajc.getTaskDependencyFromProjectDependency(true, "compileJava")

    doLast{
        System.out.println("Java Version: " + JavaVersion.current());
        ant.taskdef( resource:"org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: configurations.ajc.asPath)

        ant.iajc(
                maxmem: "1024m", fork: "true", Xlint: "ignore",
                destDir: sourceSets.main.output.classesDirs.asPath,
                //my-common.jar consist of my own AspectTracing.class
                aspectPath: "$projectDir/image/pqr-bundle-common*.jar",
                //aspectPath: configurations.aspects.asPath,
                sourceRootCopyFilter: "**/*.java",
                classpath: configurations.compile.asPath,
                source: project.sourceCompatibility,
                target: project.targetCompatibility
        ){
            sourceroots{
                sourceSets.main.java.srcDirs.each{
                    //ignoring root project and adding only sub-modules
                    if(!it.absolutePath.contains("/src/main/java")) {
                        pathelement(location: it.absolutePath)
                    }
                }
            }
        }
    }
}

The TracingAspect.java look like this. This class is a part of aspectpath jar

    <aspectpath>
       <path>
         <fileset dir="${pqr.bundle.image}" includes="pqr-bundle-common*.jar" />
       </path>
    </aspectpath>
/*
 * The pertypewithin causes all classes that don't have the Untraced annotation
 * to be woven. The pointcuts further limit the use of the tracing loggers to
 * avoid infinite recursion, etc...
 */
@Aspect("pertypewithin(!is(InterfaceType) && !is(EnumType) && ([email protected] "
        + "(my-somepackaje..* || my-anotherpackage..*)))")
@Untraced
public class TracingAspect {

    private MyLogger myLogger;
    private static final String ENTERING_PREFIX = "Entering {0}";
    private static final String EXITING_PREFIX = "Exiting {0}";
    private static final String ARGUMENTS = ": Arguments =>";
    private static final String ARGUMENTS_NOT_AVAILABLE = ARGUMENTS + " N/A";
    private static final String RETURN = ", Returns =>";
    private static final String RETURN_NOT_AVAILABLE = RETURN + " N/A";
    private static final String THROWING = "Throwing {0}";

    @Pointcut("staticinitialization(*)")
    public void staticInit() {
    }

    /**
     * This is run in a static initializer block for every woven class, and is
     * used to initialize the trace logger for the class.
     */
    @After("staticInit()")
    public void initLogger(JoinPoint.StaticPart jps) {
        myLogger = MyLogger.getLogger(jps.getSignature().getDeclaringTypeName());
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Pointcuts
    //
    ////////////////////////////////////////////////////////////////////////////

    @Pointcut("execution(new(..))")
    void tracedConstructors() {
    }

    // The !execution(String toString()) avoids infinite recursion by not
    // tracing toString() methods.
    // Also, don't trace the compile-time inserted 'access$nnn' methods. That
    // might (in very specific cases) lead to recursion but more generally just
    // confusing.
    @Pointcut("execution(* *(..)) && !execution(String toString()) && !execution(* access$*(..)) "
            + "&& !@annotation(my.common.logging.Untraced)")
    void tracedMethods() {
    }

    // Advice starts below.

    @Before("tracedConstructors()")
    public void traceConstructorEntry(JoinPoint thisJoinPoint) {
        if (null != myLogger)
            myLogger.trace(() -> entering(thisJoinPoint.getSignature().getName(), thisJoinPoint.getArgs()));
    }

    @AfterReturning("tracedConstructors()")
    public void traceConstructorExit(JoinPoint thisJoinPoint) {
        if (null != myLogger)
            myLogger.trace(() -> exiting(thisJoinPoint.getSignature().getName(), thisJoinPoint.getArgs(), null));
    }

    @AfterThrowing(pointcut = "tracedConstructors()", throwing = "t")
    public void traceConstructorThrow(JoinPoint thisJoinPoint, Throwable t) {
        if (null != myLogger)
            myLogger.trace(() -> MessageFormat.format(THROWING, thisJoinPoint.getSignature().getName()) + " - "
                    + t.toString());
    }

    @Before("tracedMethods()")
    public void traceMethodEntry(JoinPoint thisJoinPoint) {
        if (null != myLogger)
            myLogger.trace(() -> entering(thisJoinPoint.getSignature().getName(), thisJoinPoint.getArgs()));
    }

    @AfterReturning(pointcut = "tracedMethods()", returning = "r")
    public void traceMethodExit(JoinPoint thisJoinPoint, Object r) {
        // Add sensitive return value to log context
        if (((MethodSignature) thisJoinPoint.getSignature()).getMethod()
                .getAnnotation(SensitiveTraceReturn.class) != null) {
            LogContext.addSensitiveValue(r);
        }
        if (null != myLogger){
            myLogger.trace(() -> exiting(thisJoinPoint.getSignature().getName(), thisJoinPoint.getArgs(), r));
        }
    }

    @AfterThrowing(pointcut = "tracedMethods()", throwing = "t")
    public void traceMethodThrow(JoinPoint thisJoinPoint, Throwable t) {
        if (null != myLogger)
            myLogger.trace(() -> MessageFormat.format(THROWING, thisJoinPoint.getSignature().getName()) + " - "
                    + t.toString());
    }

    // End of Advice

    /**
     * Creates entering message trace statement
     *
     * @param method
     *            method name of the log entry
     * @param args
     *            {@code Object} array representing the message parameters
     */
    private String entering(String method, Object[] args) {
        StringBuffer msgBuffer = new StringBuffer(ENTERING_PREFIX);
        Object[] temp = { method };

        if (args != null && args.length > 0) {
            int index = 1;
            msgBuffer.append(ARGUMENTS);
            for (Object arg : args) {
                if (index > 1) {
                    msgBuffer.append(",");
                }
                msgBuffer.append(" {").append(index).append("}");
                index++;
            }
            temp = ArrayUtils.addAll(temp, args);
        } else {
            msgBuffer.append(ARGUMENTS_NOT_AVAILABLE);
        }

        return MessageFormat.format(msgBuffer.toString(), temp);
    }

    /**
     * Creates exiting message trace statement
     *
     * @param method
     *            method name of the log entry
     * @param args
     *            {@code Object} array representing the message parameters
     * @param returnValue
     *            return value from the method, if any
     */
    private String exiting(String method, Object[] args, Object returnValue) {
        StringBuffer msgBuffer = new StringBuffer(EXITING_PREFIX);
        Object[] temp = { method };

        int index = 1;
        if (args != null && args.length > 0) {
            msgBuffer.append(ARGUMENTS);
            for (Object arg : args) {
                if (index > 1) {
                    msgBuffer.append(",");
                }
                msgBuffer.append(" {").append(index).append("}");
                index++;
            }
            temp = ArrayUtils.addAll(temp, args);
        } else {
            msgBuffer.append(ARGUMENTS_NOT_AVAILABLE);
        }

        if (null != returnValue) {
            msgBuffer.append(RETURN).append(" {").append(index).append("}");
            temp = ArrayUtils.addAll(temp, new Object[] { returnValue });
        } else {
            msgBuffer.append(RETURN_NOT_AVAILABLE);
        }
        temp = ArrayUtils.addAll(temp, args);

        return MessageFormat.format(msgBuffer.toString(), temp);
    }
}

Where the interface :- my.common.logging.Untraced is just empty (for now)

public @interface Untraced {
}

But, it is failing during instrumentation for with random compilation error.

(Note: without aspectJ my compilation, jar creation happens properly.)

Usually, AspectJ should do log tracing weaving but it is trying to compile it again and hence failing. Any clue what am I missing here?

The Java code for which it is failing is something like below:

private static void runRule(boolean before) {
    String str = (before) ? "before" : "after" ;
}

Error is:

[error] Syntax error, insert "|| Expression" to complete Expression
[ant:iajc] String str = (before) ? "before" : "after" ;
[ant:iajc]               

Solution

  • I've pushed working example demo code here https://github.com/swapnil-kotwal-sp/basic-demo

    I also able to fix my business project issue during instrumentation, the problem was with below pice of code.

    private static void runRule(boolean before) {
        String str = (before) ? "before" : "after" ;
    }
    

    Basically AspectJ doesn't like those circular brackets around boolean before, I changed my code and AspectJ started working fine.

    private static void runRule(boolean before) {
        String str = before ? "before" : "after" ;
    }