I've not tried using aspectj in plugin development, so there might be a few additional things. But here's a few things you need to do to ensure the target is woven correctly at compile time and can be run.
- The plugin that is being woven needs to have a dependency on the plugin containing the aspect
- The aspect needs to be in the Exported Packages on the classpath
- The target plugin needs to have aspectjrt on the classpath so it can handle the aspects
- The aspectj compiler needs to be used to weave the target when it is compiled.
Update, I've been unable to reproduce your problem (i.e. it works fine on my box). To replicate the situation I created an AspectJ project with the single Logging.aj file in the source directory. I exported that as a jar file (called logging.jar) to another project's root (the other project also set up as an AspectJ project containing the "Main" class). I then modified the Aspect Path of the "main" project to include the logging.jar and the aspects and the advice was woven to each doSomethingAa() and doSomethingB() method call.
The only issue I found with your code was that your static method calls are for "A" rather than "Main".
Here is the entry from the main project's .classpath file:
<classpathentry kind="lib" path="logging.jar">
<attributes>
<attribute name="org.eclipse.ajdt.aspectpath"
value="org.eclipse.ajdt.aspectpath"/>
</attributes>
</classpathentry>
I've tried various permutations, and the only ways I can get it to not work are by removing the AspectJ nature or removing the jar from the build path.
Are there any other factors that may be affecting your workspace that you've omitted?
One other point about your logging aspect that I found in a similar project; Separate before and after advice will result in JoinPoint instances being created twice for every method call, this can cause a problem for garbage collection if your logging type weaves a lot of methods. Instead you could consider using around advice to log both the entry and exit, this also makes it easier to add in any method execution time logging if you decide to later.
Update: Based on your comments, I added a third project (aj_client) to my workspace and went through the following steps:
- Modified Logging.aj to do System.out calls, ruling out log4j configuration issues
- exported aj_logging (AspectJ project containing Logging.aj) to logging.jar
- added logging.jar to the Aspect Path of aj_target
- exported aj_target (AspectJ project containing Main.java) to target.jar
- Created a new class (Client.java) in the aj_client project (which has no AspectJ nature).
- added target.jar, logging.jar (and log4j.jar) to the Java Build Path of aj_client and ran it.
Client.java contains a single method:
public static void main(String[] args) {
Main.main(args);
}
When run, this fails with a NoClassDefFoundError:
Exception in thread "main" java.lang.NoClassDefFoundError: org/aspectj/lang/Signature
at Client.main(Client.java:6)
Caused by: java.lang.ClassNotFoundException: org.aspectj.lang.Signature
To address this, I modified the .classpath of aj_client so it has aspectjrt on it (by manually adding the AspectJ Runtime Library classpath container to the .classpath) and reran, the program executes and outputs the logging statements:
Entering function: doSomethingAa with input parameters: [java.lang.String message] = [Tal] [java.util.Map map] = [{FirstKey=FirstValue, SecondKey=SecondValue}]
log4j:WARN No appenders could be found for logger (A.class).
log4j:WARN Please initialize the log4j system properly.
Exit function: void target.Main.doSomethingAa(int, String, Map)
Entering function: doSomethingAa with input parameters: [java.lang.String message] = [Guy] [java.util.Map map] = [{Happy=Birthday, Tal=Guy}]
Exit function: void target.Main.doSomethingAa(int, String, Map)
Entering function: doSomethingB with input parameters: [java.lang.String name] = [TalG]
Exit function: void target.Main.doSomethingB(int, String)
Entering function: doSomethingB with input parameters: [java.lang.String name] = [GuyG]
Exit function: void target.Main.doSomethingB(int, String)
Finished running main
The .classpath file for aj_client looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.ajdt.core.ASPECTJRT_CONTAINER"/>
<!-- the other jars for the logging and target projects -->
<classpathentry kind="lib" path="/aj_target/target.jar"/>
<classpathentry kind="lib" path="/aj_target/log4j-1.2.14.jar"/>
<classpathentry kind="lib" path="/aj_target/logging.jar"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>
I also tried pointing to my aspectjrt in my Maven repository and Eclipse plugin, with the same result (the logging messages were output), i.e. replace:
<classpathentry kind="con" path="org.eclipse.ajdt.core.ASPECTJRT_CONTAINER"/>
with
<!--aspectjrt from Maven repository-->
<classpathentry kind="lib" path="C:/maven-2.2.0/repo/aspectj/aspectjrt/1.5.3/aspectjrt-1.5.3.jar"/>
or
<!--aspectjrt from Eclipse plugin -->
<classpathentry kind="lib" path="C:/eclipse-3.5/eclipse/plugins/org.aspectj.runtime_1.6.5.20090618034232/aspectjrt.jar"/>
Having proven that the logging code is woven, I went back and changed Logging.aj to use getLog().info() calls again, and found the logging statements are no longer output. To remedy this I added a log4j.xml configuration file (just specifying the root appender)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %c{1} - %m%n"/>
</layout>
</appender>
<root>
<priority value ="debug" />
<appender-ref ref="console" />
</root>
</log4j:configuration>
This resulted in the following output:
DEBUG class - A
INFO Logging - Exit function: void target.Main.doSomethingAa(int, String, Map)
INFO Logging - Entering function: doSomethingB with input parameters: [java.lang.String name] = [TalG]
DEBUG class - B
INFO Logging - Exit function: void target.Main.doSomethingB(int, String)
INFO Logging - Entering function: doSomethingB with input parameters: [java.lang.String name] = [GuyG]
DEBUG class - B
INFO Logging - Exit function: void target.Main.doSomethingB(int, String)
Finished running main
Note You need to be careful to ensure you have cleaned, built, and exported logging.jar before cleaning, building, and exporting target.jar, then clean the client project. If you muck up the order at all you'll get mismatched content.
Summary
So it appears as long as your client project references a "target.jar" that was built with AspectJ (so the Logging.aj was woven), and you have an aspectjrt.jar on your classpath and you have configured log4j correctly the logging will be output.
You can specify the aspectjrt dependency by either adding the classpath container, or by specifying the path to a compatible aspectjrt.jar