I have the following classes:
public class Population {
private int population;
public Population()
{
population = 0;
}
public void newYear()
{
population += 10;
}
public int getPopulation() {
return population;
}
}
and the following aspect
public aspect StatisticsAspect {
private static int year = 0;
pointcut operation(Population caller) : call(* Population.newYear());
after(Population caller) : operation(caller)
{
System.out.println("New Year: " + year);
year++;
System.out.println("Population: " + caller.getPopulation());
}
}
Now I want that everytime when newYear()
is called, that the "statistics" are printed to the console.
Unfortunately I get the ajc: formal unbound in pointcut
error.
What do I need to change to make it work?
I refactored your code a bit because getPopulation()
is a bad name and against conventions. Not the population object s returned as the name implies but the population's size.
As for your aspect, the naming caller
is also bad because the object is not the caller but the callee or the target of the call. I simply renamed the parameter to population
because this time it really contains a population object. Then I bound it to a target()
parameter in order to make your error message go away.
I also switched from call()
to execution()
pointcut because it is more efficient to weave the code into the executed method instead of into every single place where the method is called.
I also made sure that the count starts at 1, not 0, after the first new year is over and the population has grown. I did this by using ++size
rather than size++
, i.e. pre- instead of post-increment.
Now the code looks like this:
package de.scrum_master.app;
public class Population {
private int size;
public Population() {
size = 0;
}
public void newYear() {
size += 10;
}
public int getSize() {
return size;
}
public static void main(String[] args) {
Population population1 = new Population();
population1.newYear();
population1.newYear();
Population population2 = new Population();
population2.newYear();
population2.newYear();
}
}
package de.scrum_master.aspect;
import de.scrum_master.app.Population;
public aspect StatisticsAspect {
private static int year = 0;
pointcut operation(Population population) :
execution(* Population.newYear()) && target(population);
after(Population population) : operation(population) {
System.out.printf("Population for year %d = %d%n", ++year, population.getSize());
}
}
Now look at the console log:
Population for year 1 = 10
Population for year 2 = 20
Population for year 3 = 10
Population for year 4 = 20
Can you spot the problem? You have one overall year counter, but multiple populations. Actually, you should have one year counter per population in order to get your statistics right. This can be done by using one aspect instance per target (i.e. per population) instead of a singleton aspect and of course by no longer making the year counter static:
package de.scrum_master.aspect;
import de.scrum_master.app.Population;
public aspect StatisticsAspect pertarget(execution(Population.new(..))) {
private int year = 0;
pointcut operation(Population population) :
execution(* Population.newYear()) && target(population);
after(Population population) : operation(population) {
System.out.printf("%s size for year %d = %d%n", population, ++year, population.getSize());
}
}
Here, pertarget(execution(Population.new(..)))
means: Create one aspect instance per Population
constructor execution, i.e. per created object.
Now the statistics are correct (I also changed the log message a bit in order to print the object ID so we can see which message belongs to which population):
de.scrum_master.app.Population@1d44bcfa size for year 1 = 10
de.scrum_master.app.Population@1d44bcfa size for year 2 = 20
de.scrum_master.app.Population@266474c2 size for year 1 = 10
de.scrum_master.app.Population@266474c2 size for year 2 = 20