AspectJ
- Introduction : https://eclipse.org/aspectj/doc/next/progguide/starting-aspectj.html
- Getting Started : https://eclipse.org/aspectj/doc/next/progguide/starting.html
Avec Maven
Installer Maven
Créer un projet Maven
mkdir -p aj/src/main/java
Créer le fichier aj/src/main/java/fr/imag/air/aj/SimpleThreads.java
// From https://docs.oracle.com/javase/tutorial/essential/concurrency/simple.html
package fr.imag.air.aj;
public class SimpleThreads {
// Display a message, preceded by
// the name of the current thread
static void threadMessage(String message) {
String threadName =
Thread.currentThread().getName();
System.out.format("%s: %s%n",
threadName,
message);
}
private static class MessageLoop
implements Runnable {
public void run() {
String importantInfo[] = {
"Mares eat oats",
"Does eat oats",
"Little lambs eat ivy",
"A kid will eat ivy too"
};
try {
for (int i = 0;
i < importantInfo.length;
i++) {
// Pause for 4 seconds
Thread.sleep(4000);
// Print a message
threadMessage(importantInfo[i]);
}
} catch (InterruptedException e) {
threadMessage("I wasn't done!");
}
}
}
public static void main(String args[])
throws InterruptedException {
// Delay, in milliseconds before
// we interrupt MessageLoop
// thread (default one hour).
long patience = 1000 * 60 * 60;
// If command line argument
// present, gives patience
// in seconds.
if (args.length > 0) {
try {
patience = Long.parseLong(args[0]) * 1000;
} catch (NumberFormatException e) {
System.err.println("Argument must be an integer.");
System.exit(1);
}
}
threadMessage("Starting MessageLoop thread");
long startTime = System.currentTimeMillis();
Thread t = new Thread(new MessageLoop());
t.start();
threadMessage("Waiting for MessageLoop thread to finish");
// loop until MessageLoop
// thread exits
while (t.isAlive()) {
threadMessage("Still waiting...");
// Wait maximum of 1 second
// for MessageLoop thread
// to finish.
t.join(1000);
if (((System.currentTimeMillis() - startTime) > patience)
&& t.isAlive()) {
threadMessage("Tired of waiting!");
t.interrupt();
// Shouldn't be long now
// -- wait indefinitely
t.join();
}
}
threadMessage("Finally!");
}
}
Créer le fichier aj/src/main/java/fr/imag/air/aj/DurationAspect.aj
package fr.imag.air.aj;
public aspect DurationAspect {
private long startTime;
private long count = 0;
private static long globalDuration=0;
private static long globalCount = 0;
before() : execution(public void *.run()){
startTime=System.nanoTime();
count++;
globalCount++;
}
after() : execution(public void *.run()){
long endTime=System.nanoTime();
long delta = endTime-startTime;
globalDuration +=delta;
System.out.println("Call #" + count + ": Duration=" + delta/1000000);
}
before() : execution(public void SimpleThreads.main(..)){
System.out.println("START STATISTICS ");
}
after() : execution(public void SimpleThreads.main(..)){
System.out.println("END STATISTICS ");
System.out.println("CallNumber: " + globalCount + " : AvgDuration=" + (globalDuration/(globalCount*1000000)));
}
}
Créer le fichier aj/pom.xml
<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>fr.imag.air.aj</groupId>
<artifactId>examples</artifactId>
<version>0.1.0-SNAPSHOT</version>
<name>AspectJ examples</name>
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<aspectj.version>1.8.6</aspectj.version>
<exec-maven-plugin.version>1.4.0</exec-maven-plugin.version>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<configuration>
<outxml>true</outxml>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<complianceLevel>${maven.compiler.target}</complianceLevel>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>${exec-maven-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>${project.groupId}.SimpleThreads</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</project>
Build:
mvn clean install
Execution:
mvn exec:java
Exercice
Exercice 1
Ajouter une tableau de NUM_THREADS threads parallèles au programme ( (ie. le nombre de threads peut être passé au programme avec l'option -D de java : NUM_THREADS=Integer.parseInt(System.getProperty("numthreads", "10"))).
Thread t = new Thread(new MessageLoop());
t.start();
Attention: le code de l'aspect est désormais accédé concurremment : il est nécessaire de snchroniser l'accès.
Exercice 2
Transformer l'aspect précédent pour créer un quota d'utilisation du temps passé dans la méthode void run(). Un paramêtre est un pourcentage maximun par appel (cumulé depuis le démarrage du programme). La valeur du débit pourcentage est passé via les propriétés systèmes de Java (ie. Integer.parseInt(System.getProperty("throughput", "10000"))). Tester s'il y a dépassement en début d'appel (before()) et si besoin mettre en sommeil la thread la durée nécessaire (Thread.sleep(millisec)).
Exercice Bonus
Ajouter un profile au projet Maven pour tisser les aspects au chargement du programme (load-time weaving). Inspirez vous de l'exemple https://github.com/jbellmann/aspectj-examples/blob/master/loadtime-weaving/pom.xml
Bonus du Bonus
AspectJ sur un bundle OSGi.