Question

Scenario:

I have a mix of source files for classes, interfaces, and enums all together in one package like so:

package com.mycompany.data;
class Dog {
    // implementation
}

package com.mycompany.data;
class Cat {
    // implementation
}

package com.mycompany.data;
enum Gender {
    // gender options
}

package com.mycompany.data;
interface Animal {
    // methods
}

// ... plus a lot more concrete classes and a few more interfaces ...

The Goal: To have all of the classes implement a new interface and a new method.

The Problem: I can successfully weave the interface into the classes, and exclude the enums, but I can't figure out how to keep the new interface from being added on to the interfaces in the package as well.

My aspect currently looks like:

public aspect SecureAspect {
    declare parents: com.mycompany.data.* && !java.lang.Enum+ implements Secure;

    // add method from secure interface to everything implementing Secure
}

Which matches Dog, Cat, and Animal in my example.

I previously tried:

declare parents: com.mycompany.data.* && java.lang.Object+ && !java.lang.Enum+ implements Secure;

because Animal.class.getSuperclass() == null, as opposed to Object, but to no avail.

I know that I could solve this problem by moving the interfaces out of the package (which I'm happy to do if this turns out to be impossible), but I'm curious whether there's a way to exclude the interfaces like I did with the Enums.

Pretty sure it doesn't matter, but I'm using load-time weaving with the javaagent.

Was it helpful?

Solution

The question is old, but I found it interesting and did some research.

The solution looks like this:

Aspect excluding enums and interfaces from ITD:

package com.mycompany.aspect;

import com.mycompany.data.Secure;

public aspect SecureAspect {
    declare parents :
        !is(InterfaceType) && !is(EnumType) && com.mycompany.data.*
        implements Secure;

    public void Secure.doSomething() {
        System.out.println("I am a secure " + this.getClass().getSimpleName());
    }
}

BTW, enums will not implement the interface anyway even if you do not exclude them. But then you would get the compiler error "can't use declare parents to make enum type com.mycompany.data.Gender implement an interface".

Driver application verifying ITD effect:

The application iterates over all relevant classes, enums, interfaces and checks if they indeed implement the Secure interface. We expect enum Gender and interface Animal to be exempt from ITD due to the !is(InterfaceType) && !is(EnumType) clause. If the interface is indeed implemented, the doSomething method will be called via reflection in order to double-check the ITD effect.

package com.mycompany.data;

public class Application {
    public static void main(String[] args) throws Exception {
        for (Class<?> clazz : new Class[] { Application.class, Cat.class, Dog.class, Gender.class, Animal.class }) {
            String implementsYesNo = " does not implement";
            for (Class<?> iface : clazz.getInterfaces()) {
                if (iface == Secure.class) {
                    implementsYesNo = " implements";
                    Object instance = clazz.newInstance();
                    clazz.getMethod("doSomething").invoke(instance);
                    break;
                }
            }
            System.out.println(clazz.getSimpleName() + implementsYesNo + " interface Secure\n");
        }
    }
}

Console output:

I am a secure Application
Application implements interface Secure

I am a secure Cat
Cat implements interface Secure

I am a secure Dog
Dog implements interface Secure

Gender does not implement interface Secure

Animal does not implement interface Secure

Special thanks to Andy Clement, AspectJ maintainer, who pointed me to the AspectJ 1.6.9 release notes (search for "Type category type patterns") because the feature is otherwise undocumented even though it is an official part of the AspectJ language.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top