Skip to content

AspectJ

paulcleary edited this page Nov 10, 2015 · 2 revisions

Currently, there isn't a lot of reason to use AspectJ. Many projects that have worked with Money have balked at AspectJ, to the point where you can do pretty much everything you need to using other modules. However, it is a proven solution that works in production, so we continue to support it.

  • If you are writing a Java application, and would like to simplify how to do tracing.
  • If you are writing a Scala application, and you like using annotations.

The Money Scala API provides traced and timed functions that do the same thing as these annotations, so in reality, if you are writing scala and you are not making extensive use of Futures or Akka, then you only need the money-core module

money-aspectj is the foundation library for instrumenting your applications using aspectj. If you don't know what aspectj is, it is a mechanism that allows you to put common code in one place, and then selectively apply it across your codebase.
You can read more about it on the AspectJ site.

Money currently uses AspectJ 1.7.3.

Adding the dependency

Add a dependency as follows for maven:

    <dependency>
        <groupId>com.comcast.money</groupId>
        <artifactId>money-aspectj_2.10</artifactId>
        <version>${money.version}</version>
    </dependency>

And here is a dependency for SBT:

    libraryDependencies += "com.comcast.money" %% "money-aspectj" % "${money.version}"

Load-time Weaving

Load-time weaving is an awesome way to apply the aspects to your codebase. All you need to do is add a JVM argument when your application (container or whatever) starts up, and VOILA!, you are tracing (well..almost..you have to use the annotations covered below)

Of course, you need to make sure that you have the aspectj-weaver library available someplace. Money currently uses aspectj version 1.7.3, so please make sure you are using the same version, lest you might be out of luck if things go awry.

All you need to do to instrument your application to use load-time weaving is the following:

java -javaagent:/path/to/your/aspectj-weaver.jar

Maven Load Time Weaving Example

The following is an example of incorporating load-time weaving into your maven pom:

...
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.7.3</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>1.7.3</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.7.3</version>
        </dependency>
...
<!-- add the java agent to point to the aspectjweaver when tests start -->
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>2.9</version>
      <configuration>
        <argLine>-javaagent:${user.home}/.m2/repository/org/aspectj/aspectjweaver/1.7.3/aspectjweaver-1.7.3.jar</argLine>
      </configuration>
    </plugin>
 <!-- Jetty plugin, to run with aspects enabled use mvn jetty:run-forked -->
    <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>9.2.1.v20140609</version>
        <configuration>
            <scanIntervalSeconds>10</scanIntervalSeconds>
            <contextPath>/</contextPath>
            <scanIntervalSeconds>10</scanIntervalSeconds>
            <stopKey>STOP</stopKey>
            <stopPort>8005</stopPort>
            <jvmArgs>-javaagent:${user.home}/.m2/repository/org/aspectj/aspectjweaver/1.7.3/aspectjweaver-1.7.3.jar -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false</jvmArgs>
        </configuration>
    </plugin>
  </plugins>
</build>

@Traced annotation

To start using tracing in your code, simply use the @Traced annotation in com.comcast.money.annotations.Traced. The annotation takes a string value that must be provided. This is the name that is used when the trace data is emitted.

Using the @Traced annotation is a surefire way to ensure that your trace spans are always stopped. Instead of having to write it yourself like so:

  import com.comcast.money.core.Money._
  
  def somethingMeaningful() {
    try {
      tracer.startSpan("something")
      ...
    } finally {
      tracer.stopSpan()
    }
  }

You can achieve the same thing by doing this:

  import com.comcast.money.core.Money._
  import com.comcast.money.annotations._
  
  @Traced("meaningful_name")
  def somethingMeaningful() {
      ...
  }

Usage Notes

  • Be careful when you use the @Traced annotation in your code. If a method that is @Traced calls another method that also has the @Traced annotation, a child span will be started and stopped for the nested method call. This often times is desirable, but if you are not careful, you may create deeply nested trace trees unintentionally.

@TracedData annotation

This allows you to simply record the values of parameters on a method that has the @Traced annotation.

In the following example, a Note named "dataPoint" will be added to the data in the trace, along with the value of dataPoint when the method is called

  import com.comcast.money.core.Money._
  import com.comcast.money.annotations._
  
  @Traced("meaningful_name")
  def somethingMeaningful(@TracedData dataPoint:String) {
      ...
  }

Usage Notes

The @TracedData annotation is only picked up on methods that have the @Traced annotation on them (currently), although I can see how its use on any method may be desirable.

@Timed annotation

The @Timed annotation is a simple way to record the execution time of any method that is executed within a trace.

  import com.comcast.money.core.Money._
  import com.comcast.money.annotations._
  
  @Timed("chumpie")
  def chumpieInATrace() {
      ...
  }

Usage Notes

  • if there is no trace present, one will NOT be automatically created for you (i.e. nothing will be traced). Make sure your @Timed method is called within a trace
  • names provided in the annotation CAN collide within the bounds of a single trace span. So if you call the same method with the same annotation multiple times, only the last time will be recorded.