2

The following example is a reduction of the real problem in that it tries to simplify is as much as possible.

I have a java interface, and several objects that implement that interface, like:

public interface Shape{
    public void draw();
    public void erase();
    public boolean isDrawn();
}

public class Square implements Shape{
    @Override
    public void draw(){
        //TODO: method implementation
    }

    @Override
    public void erase(){
        //TODO: method implementation
    } 

    Override
    public boolean isDrawn(){
        //TODO: method implementation
        return false;
    }
}

public Triangle implements Shape{
    //same as above
}

public Circle implements Shape{
    //same as above
}

This is the structure of my program. By using AspectJ I want to have a map that holds each object that implements the interface. To do so I was trying to capture the constructors by using the following aspect:

public aspect ShapeHolderAspect{
    private Map<Integer, Shape> map = new HashMap<>();
    private int count = 0;    

    pointcut shapeInit(): call((Shape+).new(..));

    Object around(): shapeInit() {
        System.out.println("capturing new");

        Shape shapeType = (Shape)proceed();
        map.put(++count, shapeType);
        return shapeType;
    }
}

This code will work if I create a Shape using the following scenario:

public static void main(String[] args){
    Shape myShape = new Circle();
}

However, I am using java language reflection, and so technically I don't call the "new" constructor. Instead I locate the path of the package, and create the object passing a string with the name of the class:

public static void main(String[] args){
    String shapeClassName = args[0];
    Class<?> classType = Class.forName("myPackage.figures" + "." + shapeClassName);
    Shape myShape =(Shape)classType.getConstructor().newInstance();
}

By doing this way, AspectJ cannot detect that I am creating shapes. How do I fix this?

kriegaex
  • 63,017
  • 15
  • 111
  • 202
Flame_Phoenix
  • 16,489
  • 37
  • 131
  • 266
  • I just updated my initial answer which you might have seen already or not. The new answer is better and more complete than the old one. – kriegaex May 03 '13 at 07:05

2 Answers2

2

New, better version:

Well, while the old version below actually catches all constructor executions, an around advice on constructor execution returns null because the object in question has not been initialised yet. So you would end up with a map of null pointers in your aspect. In order to fix this you need to bind this() to a variable (sample code uses default package name):

public class Application {
    public static void main(String[] args) throws Exception {
        new Circle().draw();
        ((Shape) Class.forName("Triangle").getConstructor().newInstance()).isDrawn();
        ((Shape) Class.forName("Square").getConstructor().newInstance()).erase();
    }
}
import java.util.HashMap;
import java.util.Map;

public aspect ShapeHolderAspect {
    private Map<Integer, Shape> map = new HashMap<Integer, Shape>();
    private int count = 0;

    after(Shape shape): execution(Shape+.new(..)) && this(shape) {
        System.out.println(thisJoinPointStaticPart);
        map.put(++count, shape);
    }

    after() : execution(* Application.main(..)) {
        System.out.println("\nList of shapes:");
        for (int key : map.keySet())
            System.out.println("  " + key + " -> " + map.get(key));
    }
}

The output looks like this:

initialization(Circle())
initialization(Triangle())
initialization(Square())

List of shapes:
  1 -> Circle@1a2961b
  2 -> Triangle@12d03f9
  3 -> Square@5ffb18

BTW, if you absolutely need an around advice because you want to do other things before and after object creation, it would look like this:

void around(Shape shape): execution(Shape+.new(..)) && this(shape) {
    System.out.println(thisJoinPointStaticPart);
    proceed(shape);
    map.put(++count, shape);
}

Old, incomplete version:

Quite simply, just intercept constructor execution instead of call:

pointcut shapeInit(): execution(Shape+.new(..));

This way you weave into the called code (callee), not the calling code (caller). Consequently, it does not matter if the caller issues a reflective or normal call.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • This appears to work for explicit types; does it work for class types determined at runtime? For instance, I scan the classpath for all types that use a specific annotation; once I have that list, add a `pointcut` to count instances for each of those types when instantiated. see this: http://stackoverflow.com/q/28652312/791406 – raffian Feb 22 '15 at 00:06
  • Just use LTW (load-time weaving) and make sure the weaving agent is loaded before the classes you want to count, which is usually the case anyway. No need for classpath scanning or other strange tricks, AspectJ does it for you. – kriegaex Feb 22 '15 at 09:39
  • got an example for LTW? – raffian Feb 22 '15 at 18:16
  • The follow-up question is not very concrete. What is the problem? LTW usage as such? Try your favourite web search engine and see how far you get. If you have a more concrete question, I will be happy to answer it. – kriegaex Mar 15 '15 at 12:06
0

Found that the following pointcut will do the job:

pointcut lockReflectInit(): call(public Object java.lang.reflect.Constructor.newInstance(..));  

This will however catch ALL calls of newInstance, and not just the ones that return Shape =(

Flame_Phoenix
  • 16,489
  • 37
  • 131
  • 266