AspectJ

From air
Jump to navigation Jump to search



Avec Maven

Installer Maven

Créer un projet Maven en clonant le projet

git clone https://github.com/donsez/aspectj_homework

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.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <aspectj.version>1.8.13</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.11</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 versions:display-dependency-updates
mvn versions:display-plugin-updates

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

Dans cet exercice, Ajouter via un aspect la propriété non-fonctionnelle de quota d'utilisation ou rate limiting (soit par un temps max d'utilisation par unité de temps, soit par un nombre d'appel max par unité de temps ...). Il n'est pas question d'implémenter un algorithme complexe (comme le Token Bucket couramment utilisé dans les canevas de rate limiting comme celui-ci, avec Spring, avec JHipster).

Pour cela, transformer l'aspect précédent pour créer une limite au temps total passé dans la méthode void run(). Le paramètre de limite est un pourcentage maximun passé dans les appels de la méthodes par unité de temps (seconde, minute, ...). Le temps passé dans l'exécution de la méthode est cumulé depuis le démarrage du programme. La valeur du paramètre est passé via une propriété système Java (ie. Float.parseFloat(System.getProperty("realtime_limit", "0.25")) pour 25%). Pour réaliser cette limite, il suffira de tester s'il y a dépassement en début d'appel de la méthode run() et si besoin mettre en sommeil la thread pendant la durée nécessaire (Thread.sleep(millisec)) puis de reprendre l'exécution de la thread.

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.