Skip to content

Commit

Permalink
ArC: allow putting bean enablement annotations on stereotypes
Browse files Browse the repository at this point in the history
It would probably be best to write this algorithm in a lazy fashion
(driven by the annotation transformation demands), but that would
require breaking an extension API (specifically, it wouldn't be
possible to produce `BuildTimeConditionBuildItem`). Hence, this commit
enhances the eager algorithm for bean enablement scanning to also
scan stereotypes, relying on subclass information in the Jandex index
to support `@Inherited` stereotypes.
  • Loading branch information
Ladicek committed Nov 24, 2023
1 parent 38ac518 commit 272cce6
Show file tree
Hide file tree
Showing 11 changed files with 1,526 additions and 136 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public BuildTimeConditionBuildItem(AnnotationTarget target, boolean enabled) {
this.target = target;
break;
default:
throw new IllegalArgumentException("'target' can only be a class, a field or a method");
throw new IllegalArgumentException("'target' can only be a class, a field or a method: " + target);
}
this.enabled = enabled;
}
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
package io.quarkus.arc.test.profile;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Set;
import java.util.stream.Collectors;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Any;
import jakarta.enterprise.inject.Instance;
import jakarta.enterprise.inject.Produces;
import jakarta.enterprise.inject.Stereotype;
import jakarta.inject.Inject;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.profile.IfBuildProfile;
import io.quarkus.test.QuarkusUnitTest;

public class IfBuildProfileStereotypeTest {
@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(DevOnly.class, InheritableDevOnly.class, TransitiveDevOnly.class,
InheritableTransitiveDevOnly.class, MyService.class, DevOnlyMyService.class,
InheritableDevOnlyMyService.class, TransitiveDevOnlyMyService.class,
InheritableTransitiveDevOnlyMyService.class, MyServiceSimple.class,
MyServiceDevOnlyDirect.class, MyServiceDevOnlyTransitive.class,
MyServiceDevOnlyOnSuperclassNotInheritable.class,
MyServiceDevOnlyOnSuperclassInheritable.class,
MyServiceDevOnlyTransitiveOnSuperclassNotInheritable.class,
MyServiceDevOnlyTransitiveOnSuperclassInheritable.class, Producers.class));

@Inject
@Any
Instance<MyService> services;

@Test
public void test() {
Set<String> hello = services.stream().map(MyService::hello).collect(Collectors.toSet());
Set<Object> expected = Set.of(
MyServiceSimple.class.getSimpleName(),
MyServiceDevOnlyOnSuperclassNotInheritable.class.getSimpleName(),
MyServiceDevOnlyTransitiveOnSuperclassNotInheritable.class.getSimpleName(),
Producers.SIMPLE);
assertEquals(expected, hello);
}

@IfBuildProfile("dev")
@Stereotype
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DevOnly {
}

@IfBuildProfile("dev")
@Stereotype
@Inherited
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface InheritableDevOnly {
}

@DevOnly
@Stereotype
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface TransitiveDevOnly {
}

@DevOnly
@Stereotype
@Inherited
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface InheritableTransitiveDevOnly {
}

interface MyService {
String hello();
}

@DevOnly
static abstract class DevOnlyMyService implements MyService {
}

@InheritableDevOnly
static abstract class InheritableDevOnlyMyService implements MyService {
}

@TransitiveDevOnly
static abstract class TransitiveDevOnlyMyService implements MyService {
}

@InheritableTransitiveDevOnly
static abstract class InheritableTransitiveDevOnlyMyService implements MyService {
}

@ApplicationScoped
static class MyServiceSimple implements MyService {
@Override
public String hello() {
return MyServiceSimple.class.getSimpleName();
}
}

@ApplicationScoped
@DevOnly
static class MyServiceDevOnlyDirect implements MyService {
@Override
public String hello() {
return MyServiceDevOnlyDirect.class.getSimpleName();
}
}

@ApplicationScoped
@TransitiveDevOnly
static class MyServiceDevOnlyTransitive implements MyService {
@Override
public String hello() {
return MyServiceDevOnlyTransitive.class.getSimpleName();
}
}

@ApplicationScoped
static class MyServiceDevOnlyOnSuperclassNotInheritable extends DevOnlyMyService {
@Override
public String hello() {
return MyServiceDevOnlyOnSuperclassNotInheritable.class.getSimpleName();
}
}

@ApplicationScoped
static class MyServiceDevOnlyOnSuperclassInheritable extends InheritableDevOnlyMyService {
@Override
public String hello() {
return MyServiceDevOnlyOnSuperclassInheritable.class.getSimpleName();
}
}

@ApplicationScoped
static class MyServiceDevOnlyTransitiveOnSuperclassNotInheritable extends TransitiveDevOnlyMyService {
@Override
public String hello() {
return MyServiceDevOnlyTransitiveOnSuperclassNotInheritable.class.getSimpleName();
}
}

@ApplicationScoped
static class MyServiceDevOnlyTransitiveOnSuperclassInheritable extends InheritableTransitiveDevOnlyMyService {
@Override
public String hello() {
return MyServiceDevOnlyTransitiveOnSuperclassInheritable.class.getSimpleName();
}
}

@ApplicationScoped
static class Producers {
static final String SIMPLE = "Producers.simple";
static final String DEV_ONLY_DIRECT = "Producers.devOnlyDirect";
static final String DEV_ONLY_TRANSITIVE = "Producers.devOnlyTransitive";

@Produces
MyService simple = new MyService() {
@Override
public String hello() {
return SIMPLE;
}
};

@Produces
@DevOnly
MyService devOnlyDirect = new MyService() {
@Override
public String hello() {
return DEV_ONLY_DIRECT;
}
};

@Produces
@TransitiveDevOnly
MyService devOnlyTransitive = new MyService() {
@Override
public String hello() {
return DEV_ONLY_TRANSITIVE;
}
};
}
}
Loading

0 comments on commit 272cce6

Please sign in to comment.