Skip to content

Commit bb2362f

Browse files
committed
Implement SyntheticBeanBuilder.withInjectionPoint() and SyntheticInjections
Bump Weld API to 7.0.Alpha6. Implement the new CDI 5.0 API for declaring and validating injection points on synthetic beans.
1 parent ed2e33f commit bb2362f

54 files changed

Lines changed: 1304 additions & 3 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
<spotbugs-maven-plugin.version>4.9.8.2</spotbugs-maven-plugin.version>
9191
<spotbugs-annotations-version>4.9.8</spotbugs-annotations-version>
9292
<testng.version>7.9.0</testng.version>
93-
<weld.api.version>7.0.Alpha5</weld.api.version>
93+
<weld.api.version>7.0.Alpha6</weld.api.version>
9494
<wildfly.arquillian.version>5.1.0.Final</wildfly.arquillian.version>
9595
</properties>
9696

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.jboss.weld.tests.bce.syntheticInjectionPoint.basic;
2+
3+
import jakarta.enterprise.context.ApplicationScoped;
4+
5+
@ApplicationScoped
6+
public class Alpha {
7+
public String ping() {
8+
return "alpha";
9+
}
10+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package org.jboss.weld.tests.bce.syntheticInjectionPoint.basic;
2+
3+
import static java.lang.annotation.ElementType.FIELD;
4+
import static java.lang.annotation.ElementType.METHOD;
5+
import static java.lang.annotation.ElementType.PARAMETER;
6+
import static java.lang.annotation.ElementType.TYPE;
7+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
8+
9+
import java.lang.annotation.Retention;
10+
import java.lang.annotation.Target;
11+
12+
import jakarta.enterprise.util.AnnotationLiteral;
13+
import jakarta.inject.Qualifier;
14+
15+
@Qualifier
16+
@Target({ TYPE, METHOD, PARAMETER, FIELD })
17+
@Retention(RUNTIME)
18+
public @interface AnotherQualifier {
19+
class Literal extends AnnotationLiteral<AnotherQualifier> implements AnotherQualifier {
20+
public static final Literal INSTANCE = new Literal();
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package org.jboss.weld.tests.bce.syntheticInjectionPoint.basic;
2+
3+
import java.lang.annotation.Annotation;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
7+
import jakarta.enterprise.context.ApplicationScoped;
8+
import jakarta.enterprise.inject.build.compatible.spi.BeanInfo;
9+
import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension;
10+
import jakarta.enterprise.inject.build.compatible.spi.Registration;
11+
import jakarta.enterprise.inject.build.compatible.spi.Synthesis;
12+
import jakarta.enterprise.inject.build.compatible.spi.SyntheticComponents;
13+
import jakarta.enterprise.inject.build.compatible.spi.Types;
14+
import jakarta.enterprise.lang.model.AnnotationInfo;
15+
16+
public class BasicSyntheticInjectionPointExtension implements BuildCompatibleExtension {
17+
18+
// Captured during @Registration for use in @Synthesis with AnnotationInfo overloads
19+
private final List<AnnotationInfo> charlieQualifiers = new ArrayList<>();
20+
21+
@Registration(types = Charlie.class)
22+
public void captureCharlieQualifiers(BeanInfo bean) {
23+
// Capture @MyQualifier and @AnotherQualifier as AnnotationInfo from the bean metadata
24+
for (AnnotationInfo qualifier : bean.qualifiers()) {
25+
String name = qualifier.declaration().name();
26+
if (name.endsWith("MyQualifier") || name.endsWith("AnotherQualifier")) {
27+
charlieQualifiers.add(qualifier);
28+
}
29+
}
30+
}
31+
32+
@Synthesis
33+
public void register(SyntheticComponents syn, Types types) {
34+
// Old API, no withInjectionPoint
35+
syn.addBean(SyntheticResult.class)
36+
.type(SyntheticResult.class)
37+
.qualifier(OldApiQualifier.class)
38+
.scope(ApplicationScoped.class)
39+
.createWith(OldApiCreator.class);
40+
41+
// withInjectionPoint(Class, Annotation...) — @Default Alpha
42+
syn.addBean(SyntheticResult.class)
43+
.type(SyntheticResult.class)
44+
.qualifier(Scenario2aQualifier.class)
45+
.scope(ApplicationScoped.class)
46+
.withInjectionPoint(Alpha.class, new Annotation[0])
47+
.createWith(NewApiClassAnnotationCreator.class);
48+
49+
// withInjectionPoint(Class, AnnotationInfo...) — 2 qualifiers
50+
// Uses AnnotationInfo captured from Charlie's bean metadata during @Registration
51+
syn.addBean(SyntheticResult.class)
52+
.type(SyntheticResult.class)
53+
.qualifier(Scenario2bQualifier.class)
54+
.scope(ApplicationScoped.class)
55+
.withInjectionPoint(Charlie.class,
56+
charlieQualifiers.toArray(new AnnotationInfo[0]))
57+
.createWith(NewApiClassAnnotationInfoCreator.class);
58+
59+
// withInjectionPoint(Type, Annotation...) — single qualifier
60+
syn.addBean(SyntheticResult.class)
61+
.type(SyntheticResult.class)
62+
.qualifier(Scenario3aQualifier.class)
63+
.scope(ApplicationScoped.class)
64+
.withInjectionPoint(types.of(Bravo.class), MyQualifier.Literal.INSTANCE)
65+
.createWith(NewApiTypeAnnotationCreator.class);
66+
67+
// withInjectionPoint(Type, AnnotationInfo...) — 2 qualifiers
68+
// Uses AnnotationInfo captured from Charlie's bean metadata during @Registration
69+
syn.addBean(SyntheticResult.class)
70+
.type(SyntheticResult.class)
71+
.qualifier(Scenario3bQualifier.class)
72+
.scope(ApplicationScoped.class)
73+
.withInjectionPoint(types.of(Charlie.class),
74+
charlieQualifiers.toArray(new AnnotationInfo[0]))
75+
.createWith(NewApiTypeAnnotationInfoCreator.class);
76+
77+
// @Dependent synthetic bean that looks up a @Dependent helper via SyntheticInjections.
78+
// When this bean is destroyed, the dependent helper should also be destroyed.
79+
syn.addBean(SyntheticResult.class)
80+
.type(SyntheticResult.class)
81+
.qualifier(DependentCleanupQualifier.class)
82+
.scope(jakarta.enterprise.context.Dependent.class)
83+
.withInjectionPoint(DependentHelper.class, new Annotation[0])
84+
.createWith(DependentCleanupCreator.class);
85+
}
86+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package org.jboss.weld.tests.bce.syntheticInjectionPoint.basic;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertNotNull;
5+
6+
import jakarta.enterprise.inject.Instance;
7+
import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension;
8+
import jakarta.enterprise.inject.spi.BeanContainer;
9+
import jakarta.inject.Inject;
10+
11+
import org.jboss.arquillian.container.test.api.Deployment;
12+
import org.jboss.arquillian.junit.Arquillian;
13+
import org.jboss.shrinkwrap.api.Archive;
14+
import org.jboss.shrinkwrap.api.BeanArchive;
15+
import org.jboss.shrinkwrap.api.ShrinkWrap;
16+
import org.jboss.weld.test.util.Utils;
17+
import org.junit.Test;
18+
import org.junit.runner.RunWith;
19+
20+
@RunWith(Arquillian.class)
21+
public class BasicSyntheticInjectionPointTest {
22+
23+
@Deployment
24+
public static Archive<?> deploy() {
25+
return ShrinkWrap.create(BeanArchive.class,
26+
Utils.getDeploymentNameAsHash(BasicSyntheticInjectionPointTest.class))
27+
.addPackage(BasicSyntheticInjectionPointTest.class.getPackage())
28+
.addAsServiceProvider(BuildCompatibleExtension.class,
29+
BasicSyntheticInjectionPointExtension.class);
30+
}
31+
32+
@Inject
33+
BeanContainer container;
34+
35+
@Test
36+
public void testOldApi() {
37+
SyntheticResult result = (SyntheticResult) container.createInstance()
38+
.select(SyntheticResult.class, new OldApiQualifier() {
39+
@Override
40+
public Class<? extends java.lang.annotation.Annotation> annotationType() {
41+
return OldApiQualifier.class;
42+
}
43+
}).get();
44+
assertNotNull(result);
45+
assertEquals("old:alpha", result.getValue());
46+
}
47+
48+
@Test
49+
public void testNewApiClassAnnotation() {
50+
SyntheticResult result = (SyntheticResult) container.createInstance()
51+
.select(SyntheticResult.class, new Scenario2aQualifier() {
52+
@Override
53+
public Class<? extends java.lang.annotation.Annotation> annotationType() {
54+
return Scenario2aQualifier.class;
55+
}
56+
}).get();
57+
assertNotNull(result);
58+
assertEquals("newClassAnnotation:alpha", result.getValue());
59+
}
60+
61+
@Test
62+
public void testNewApiClassAnnotationInfoTwoQualifiers() {
63+
SyntheticResult result = (SyntheticResult) container.createInstance()
64+
.select(SyntheticResult.class, new Scenario2bQualifier() {
65+
@Override
66+
public Class<? extends java.lang.annotation.Annotation> annotationType() {
67+
return Scenario2bQualifier.class;
68+
}
69+
}).get();
70+
assertNotNull(result);
71+
assertEquals("newClassAnnotationInfo:charlie", result.getValue());
72+
}
73+
74+
@Test
75+
public void testNewApiTypeAnnotation() {
76+
SyntheticResult result = (SyntheticResult) container.createInstance()
77+
.select(SyntheticResult.class, new Scenario3aQualifier() {
78+
@Override
79+
public Class<? extends java.lang.annotation.Annotation> annotationType() {
80+
return Scenario3aQualifier.class;
81+
}
82+
}).get();
83+
assertNotNull(result);
84+
assertEquals("newTypeAnnotation:bravo", result.getValue());
85+
}
86+
87+
@Test
88+
public void testNewApiTypeAnnotationInfoTwoQualifiers() {
89+
SyntheticResult result = (SyntheticResult) container.createInstance()
90+
.select(SyntheticResult.class, new Scenario3bQualifier() {
91+
@Override
92+
public Class<? extends java.lang.annotation.Annotation> annotationType() {
93+
return Scenario3bQualifier.class;
94+
}
95+
}).get();
96+
assertNotNull(result);
97+
assertEquals("newTypeAnnotationInfo:charlie", result.getValue());
98+
}
99+
100+
@Test
101+
public void testDependentInstanceCleanedUpOnSyntheticBeanDestruction() {
102+
DependentHelper.reset();
103+
assertEquals(0, DependentHelper.destroyedCounter.get());
104+
105+
Instance<Object> instance = container.createInstance();
106+
Instance.Handle<SyntheticResult> handle = instance
107+
.select(SyntheticResult.class, DependentCleanupQualifier.Literal.INSTANCE)
108+
.getHandle();
109+
110+
SyntheticResult result = handle.get();
111+
assertNotNull(result);
112+
assertEquals("dependentCleanup:dependent", result.getValue());
113+
114+
// The DependentHelper obtained via SyntheticInjections.get() should
115+
// still be alive — the synthetic bean hasn't been destroyed yet
116+
assertEquals(0, DependentHelper.destroyedCounter.get());
117+
118+
// Destroying the synthetic bean should also destroy the dependent helper
119+
handle.destroy();
120+
assertEquals(1, DependentHelper.destroyedCounter.get());
121+
}
122+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.jboss.weld.tests.bce.syntheticInjectionPoint.basic;
2+
3+
import jakarta.enterprise.context.ApplicationScoped;
4+
5+
@ApplicationScoped
6+
@MyQualifier
7+
public class Bravo {
8+
public String ping() {
9+
return "bravo";
10+
}
11+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.jboss.weld.tests.bce.syntheticInjectionPoint.basic;
2+
3+
import jakarta.enterprise.context.ApplicationScoped;
4+
5+
@ApplicationScoped
6+
@MyQualifier
7+
@AnotherQualifier
8+
public class Charlie {
9+
public String ping() {
10+
return "charlie";
11+
}
12+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.jboss.weld.tests.bce.syntheticInjectionPoint.basic;
2+
3+
import jakarta.enterprise.inject.build.compatible.spi.Parameters;
4+
import jakarta.enterprise.inject.build.compatible.spi.SyntheticBeanCreator;
5+
import jakarta.enterprise.inject.build.compatible.spi.SyntheticInjections;
6+
7+
/**
8+
* Looks up a @Dependent bean via SyntheticInjections during creation.
9+
* The dependent instance should be destroyed when this synthetic bean is destroyed.
10+
*/
11+
public class DependentCleanupCreator implements SyntheticBeanCreator<SyntheticResult> {
12+
@Override
13+
public SyntheticResult create(SyntheticInjections injections, Parameters params) {
14+
DependentHelper helper = injections.get(DependentHelper.class);
15+
return new SyntheticResult("dependentCleanup:" + helper.ping());
16+
}
17+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.jboss.weld.tests.bce.syntheticInjectionPoint.basic;
2+
3+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
4+
5+
import java.lang.annotation.Retention;
6+
7+
import jakarta.enterprise.util.AnnotationLiteral;
8+
import jakarta.inject.Qualifier;
9+
10+
@Qualifier
11+
@Retention(RUNTIME)
12+
public @interface DependentCleanupQualifier {
13+
class Literal extends AnnotationLiteral<DependentCleanupQualifier> implements DependentCleanupQualifier {
14+
public static final Literal INSTANCE = new Literal();
15+
}
16+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.jboss.weld.tests.bce.syntheticInjectionPoint.basic;
2+
3+
import java.util.concurrent.atomic.AtomicInteger;
4+
5+
import jakarta.annotation.PreDestroy;
6+
import jakarta.enterprise.context.Dependent;
7+
8+
@Dependent
9+
public class DependentHelper {
10+
public static final AtomicInteger destroyedCounter = new AtomicInteger(0);
11+
12+
public static void reset() {
13+
destroyedCounter.set(0);
14+
}
15+
16+
public String ping() {
17+
return "dependent";
18+
}
19+
20+
@PreDestroy
21+
public void destroy() {
22+
destroyedCounter.incrementAndGet();
23+
}
24+
}

0 commit comments

Comments
 (0)