Skip to content

Commit 30e4359

Browse files
committed
feat: add RequiresConditionExtension to veto beans on failed conditions
1 parent 001a121 commit 30e4359

1 file changed

Lines changed: 117 additions & 0 deletions

File tree

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package com.otavio.jakarta.cdi.extension;
2+
3+
import com.otavio.jakarta.cdi.Condition;
4+
import com.otavio.jakarta.cdi.RequiresCondition;
5+
import jakarta.enterprise.event.Observes;
6+
import jakarta.enterprise.inject.spi.Annotated;
7+
import jakarta.enterprise.inject.spi.BeanAttributes;
8+
import jakarta.enterprise.inject.spi.DefinitionException;
9+
import jakarta.enterprise.inject.spi.Extension;
10+
import jakarta.enterprise.inject.spi.ProcessBeanAttributes;
11+
12+
import java.lang.reflect.InvocationTargetException;
13+
import java.util.logging.Level;
14+
import java.util.logging.Logger;
15+
16+
/**
17+
* Portable extension that disables CDI beans annotated with
18+
* {@link RequiresCondition} when the declared {@link Condition} evaluates to
19+
* {@code false}.
20+
* <p>
21+
* This extension observes {@link ProcessBeanAttributes} so the condition can be
22+
* applied to bean classes, producer methods, and producer fields.
23+
* </p>
24+
*/
25+
public class RequiresConditionExtension implements Extension {
26+
27+
private static final Logger LOGGER = Logger.getLogger(RequiresConditionExtension.class.getName());
28+
/**
29+
* Evaluates {@link RequiresCondition} before the bean participates in CDI
30+
* typesafe resolution.
31+
*
32+
* @param event the process bean attributes event
33+
* @param <T> the bean type
34+
*/
35+
<T> void processBeanAttributes(@Observes ProcessBeanAttributes<T> event) {
36+
Annotated annotated = event.getAnnotated();
37+
RequiresCondition requiresCondition = annotated.getAnnotation(RequiresCondition.class);
38+
39+
if (requiresCondition == null) {
40+
return;
41+
}
42+
43+
Class<? extends Condition> conditionClass = requiresCondition.value();
44+
BeanAttributes<T> beanAttributes = event.getBeanAttributes();
45+
46+
LOGGER.log(Level.FINE,
47+
"Evaluating condition {0} for CDI bean with types {1} and qualifiers {2}",
48+
new Object[]{
49+
conditionClass.getName(),
50+
beanAttributes.getTypes(),
51+
beanAttributes.getQualifiers()
52+
});
53+
54+
Condition condition = createCondition(conditionClass);
55+
boolean matches = evaluateCondition(condition, conditionClass, beanAttributes);
56+
57+
if (!matches) {
58+
LOGGER.log(Level.INFO,
59+
"Vetoing CDI bean with types {0} because condition {1} evaluated to false",
60+
new Object[]{
61+
beanAttributes.getTypes(),
62+
conditionClass.getName()
63+
});
64+
event.veto();
65+
return;
66+
}
67+
68+
LOGGER.log(Level.FINE,
69+
"CDI bean with types {0} remains enabled because condition {1} evaluated to true",
70+
new Object[]{
71+
beanAttributes.getTypes(),
72+
conditionClass.getName()
73+
});
74+
}
75+
76+
private Condition createCondition(Class<? extends Condition> conditionClass) {
77+
try {
78+
return conditionClass.getConstructor().newInstance();
79+
} catch (NoSuchMethodException exception) {
80+
String message = "Condition class must declare a public no-argument constructor: "
81+
+ conditionClass.getName();
82+
83+
LOGGER.log(Level.SEVERE, message, exception);
84+
throw new DefinitionException(message, exception);
85+
} catch (InstantiationException | IllegalAccessException exception) {
86+
String message = "Condition class could not be instantiated: "
87+
+ conditionClass.getName();
88+
89+
LOGGER.log(Level.SEVERE, message, exception);
90+
throw new DefinitionException(message, exception);
91+
} catch (InvocationTargetException exception) {
92+
Throwable cause = exception.getCause();
93+
94+
String message = "Condition constructor failed: "
95+
+ conditionClass.getName();
96+
97+
LOGGER.log(Level.SEVERE, message, cause);
98+
throw new DefinitionException(message, cause);
99+
}
100+
}
101+
102+
private boolean evaluateCondition(Condition condition,
103+
Class<? extends Condition> conditionClass,
104+
BeanAttributes<?> beanAttributes) {
105+
try {
106+
return condition.test();
107+
} catch (RuntimeException exception) {
108+
String message = "Condition evaluation failed for "
109+
+ conditionClass.getName()
110+
+ " while evaluating CDI bean with types "
111+
+ beanAttributes.getTypes();
112+
113+
LOGGER.log(Level.SEVERE, message, exception);
114+
throw new DefinitionException(message, exception);
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)