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