Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AspectJ and CDI

I'm trying to figure out a way to inject a bean into an aspect.

I mean

public class Greeter {
    public String greet(String name) {....}
}

...

public aspect GreeterAspect {
    @Inject
    private Greeter greeter

    ...
}

Executing that as a JUnit test with Arquillian + Wildfly 8.2.1 (managed and remote) I get these lines of log:

WELD-000119: Not generating any bean definitions from x.y.z.Greeter because of underlying class loading error: Type org.aspectj.runtime.internal.AroundClosure from [Module "deployment.test.war:main" from Service Module Loader] not found.
WELD-000119: Not generating any bean definitions from x.y.z.GreeterAspect because of underlying class loading error: Type org.aspectj.lang.NoAspectBoundException from [Module "deployment.test.war:main" from Service Module Loader] not found.

and soon after I get the error

WELD-001474: Class x.y.z.Greeter is on the classpath, but was ignored because a class it references was not found: org.aspectj.runtime.internal.AroundClosure from [Module "deployment.test.war:main" from Service Module Loader].

If I get it right, it complains that aspectjrt.jar is not in the classpath, though I've checked and I got it in the dependencies (using Maven to build). Was in provided scope, tried to switch to compile but nothing changed.

Can anyone help me solve the issue?

EDIT: Solved the initial problm, now NullPointerException

Solved the initial issue by adding the aspectjrt.jar to Arquillian deployment as suggested by simas_ch.

Though, when executing, I receive a NullPointerException

public class Greeter {
    public String greet(String name) {....}
}

...

public aspect GreeterAspect {
    @Inject
    private Greeter greeter;

    private pointcut pc() : execution(* x.y.z.SomeClass.someMethod(..));

    String around() : pc() {
        log.debug("Aspect is about to say something...");
        String result = greeter.greet("Stefano");
        log.debug("Aspect said: " + result);
        return proceed();
    }
}

I can see the first log line (Aspect is about to say something...) and then I get the NullPointerException, clearly the Greeter bean has not been injected.

What am I doing wrong? Or is it possible at all to inject beans into aspects?

like image 987
Stefano Cazzola Avatar asked Nov 16 '25 01:11

Stefano Cazzola


2 Answers

I'm not familiar with CDI, but if it's not picking up the aspect as a candidate for dependency injection, you should set it manually, preferably as soon as the aspect's dependencies are ready. You can gain access to an aspect (singleton by default), with AspectName.aspectOf().

Maybe a startup singleton bean similar to this one:

@Singleton
@Startup
public class GreeterAspectSetup {

    @Inject
    private Greeter greeter;

    @PostConstruct
    private void setupGreeterAspect() {
        GreeterAspect.aspectOf().setGreeter(greeter);
    }

}

Of course, you would have to add the setter for the Greeter to the aspect, or change the field's visibility in the aspect and set it directly.

like image 196
Nándor Előd Fekete Avatar answered Nov 17 '25 19:11

Nándor Előd Fekete


Thanks to the help of the community, I managed to come out with a solution for both the problems. Leaving track here.

PART ONE - aspectjrt.jar in deployment

First, added Shrinkwrap to my dependencies:

<dependency>
    <groupId>org.jboss.shrinkwrap.resolver</groupId>
    <artifactId>shrinkwrap-resolver-api-maven</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.jboss.shrinkwrap.resolver</groupId>
    <artifactId>shrinkwrap-resolver-impl-maven</artifactId>
          <scope>test</scope>
</dependency>
<dependency>
       <groupId>org.jboss.shrinkwrap.resolver</groupId>
       <artifactId>shrinkwrap-resolver-impl-maven-archive</artifactId>
      <scope>test</scope>
</dependency>

<version> is not needed: Arquillian's BOM - already included - will take care of that.

Then add aspectj to deployment classpath:

@RunWith(Arquillian.class)
public class ArquillianTest {
    private static final String[] DEPENDENCIES = {
        "org.aspectj:aspectjrt:1.8.7"
    };

    @Deployment
    public static JavaArchive createEnvironement() {
        JavaArchive lib = ShrinkWrap.create(JavaArchive.class, "libs.jar");
        for (String dependency : DEPENDENCIES) {
            lib.merge(Maven.resolver().resolve(dependency).withTransitivity().asSingle(JavaArchive.class));
        }

        JavaArchive jar = ShrinkWrap.create(JavaArchive.class)
            // create you deployment here
            .as(JavaArchive.class);

        JavaArchive toBeDeployed = jar.merge(lib);

        return toBeDeployed;
    }

    // other stuff, like tests

}

PART TWO: Injecting a bean into an aspect

After further inquiries I think simas_ch was correct in saying that CDI does not inject beans into aspects.

Came out with a workaround: adding an @Injected member into a bean via the aspect.

public interface Advised {
    String buildGreeting(String name);
}

public class AdvisedImpl implements Advised {
    String buildGreeting(String name) {
        return "ADVISED";
    }
}

public class Greeter {
    public String greet(String name) {
        return "Hello, " + name + ".";
    }
}

...

public aspect GreeterAspect {
    @Inject
    private Greeter Advised.greeter; // adding the member to the interface / class. No need for getters / setters

    private pointcut pc() : execution(* x.y.z.Advised.buildGreeting(String));

    String around(Advised adv, String name) : pc() && target(adv) && args(name) {
        log.debug("Aspect is about to say something...");
        String result = proceed(adv, name) + " - " + adv.greeter.greet(name);
        log.debug("Aspect said: '" + result + "'");
        return result;
    }
}

Given the test

@Test
public void test() {
    assertThat(advised, not(is(nullValue())));
    assertThat(advised.buildGreeting("Stefano"), equalToIgnoringCase("advised - hello, stefano."));
}

it succeeds.

like image 36
Stefano Cazzola Avatar answered Nov 17 '25 18:11

Stefano Cazzola



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!