Skip to content

Commit

Permalink
improve extension API and resolve all remaining open questions
Browse files Browse the repository at this point in the history
- settled on the name "Build Compatible Extensions"
- renamed `@Processing` to `@Registration`, because if we ever want
  to introduce an extension phase for modifying beans before they
  are registered, _that_ should be called `@Processing`
- moved the synthetic bean creation/destruction functions from
  the `CreationalContext` style to the `Instance` style
- specified precisely how `ClassInfo.methods` and `fields` work,
  specified that `ClassConfig` behaves identically, and specified
  that an extension methods with parameters of type `MethodInfo`
  or `FieldInfo` also behave identically
- removed the `@ExactType` and `@SubtypesOf` annotations and moved
  type queries directly to the `@Enhancement` and `@Registration`
  annotations
- moved the `MetaAnnotations` API from accepting a callback
  to directly returning `ClassConfig`
- specified lifecycle of `SyntheticBean{Creator,Disposer}` and
  `SyntheticObserver`
- specified that `Messages.error` is treated as a deployment problem
  • Loading branch information
Ladicek committed Oct 18, 2021
1 parent ab69fe4 commit 93dc036
Show file tree
Hide file tree
Showing 24 changed files with 333 additions and 368 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public interface BeanInfo {
*
* @return immutable collection of qualifiers, never {@code null}
*/
// TODO method(s) for getting AnnotationInfo for given qualifier class?
Collection<AnnotationInfo> qualifiers();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,34 @@
/**
* Build compatible extensions are service providers for this interface, as defined in {@link java.util.ServiceLoader}.
* This means: they are classes that implement this interface, provide a {@code META-INF/services} file,
* and satisfy all other service provider constraints. Additionally, extensions should not be CDI beans
* and should not be used at application runtime.
* and satisfy all other service provider constraints. Additionally, build compatible extensions must not be beans
* and must not be referred to by application code.
* <p>
* Extensions can define arbitrary {@code public}, non-{@code static}, {@code void}-returning methods
* without type parameters, annotated with one of the extension annotations.
* Build compatible extensions may define arbitrary {@code public}, non-{@code static}, {@code void}-returning methods
* without type parameters, annotated with one of the extension annotations. Such methods are called extension methods.
* <p>
* Extension processing occurs in 5 phases, corresponding to 5 extension annotations:
* Extension methods are executed in 5 phases, corresponding to 5 extension annotations:
* <ul>
* <li>{@link Discovery @Discovery}</li>
* <li>{@link Enhancement @Enhancement}</li>
* <li>{@link Processing @Processing}</li>
* <li>{@link Registration @Registration}</li>
* <li>{@link Synthesis @Synthesis}</li>
* <li>{@link Validation @Validation}</li>
* </ul>
* <p>
* These methods can declare arbitrary number of parameters. Which parameters can be declared depends
* on the particular processing phase and is documented in the corresponding extension annotation.
* All the parameters will be provided by the container when the extension is invoked.
* Extension methods may declare arbitrary number of parameters. Which parameters may be declared depends
* on the particular execution phase and is documented in the corresponding extension annotation.
* All the parameters will be provided by the container when the extension method is executed.
* <p>
* Extension methods can be assigned a priority using {@link jakarta.annotation.Priority @Priority}.
* Extension methods may be assigned a priority using {@link jakarta.annotation.Priority @Priority}.
* Extension methods with smaller priority values are invoked first. Extension methods without specified priority
* have a default priority of {@link jakarta.interceptor.Interceptor.Priority#APPLICATION Priority.APPLICATION} + 500.
* If two extension methods have equal priority, the ordering among them is undefined. Note that priority
* only affects order of extensions in a single phase.
* only affects order of extension methods in a single phase.
* <p>
* If the extension declares multiple methods, they are all invoked on the same instance of the class.
* If the extension declares multiple extension methods, they are all invoked on the same instance of the class.
* <p>
* Extension classes can be annotated {@link SkipIfPortableExtensionPresent @SkipIfPortablExtensionPresent}
* Build compatible extensions may be annotated {@link SkipIfPortableExtensionPresent @SkipIfPortableExtensionPresent}
* when they are supposed to be ignored in presence of a given portable extension.
* <p>
* CDI implementations are not required to accept custom implementations of any {@code jakarta.enterprise.lang.model}
Expand All @@ -40,8 +40,4 @@
* @since 4.0
*/
public interface BuildCompatibleExtension {
// TODO rename? "build compatible" is too long; ideally, we'd have a single word that describes
// the true nature of the "new" extension API (which IMHO is: there's a barrier between extension execution
// and application execution, there's only a very narrow way how to pass information from extension
// to application, and there's _no way whatsoever_ to pass anything in the other direction)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@
* @since 4.0
*/
public interface ClassConfig extends DeclarationConfig {
// TODO now that ClassInfo also returns inherited annotations, need to think about what happens
// when we add an annotation that collides with an inherited one, or when we remove an inherited annotation

/**
* Returns the {@link ClassInfo} corresponding to this transformed class.
*
Expand Down Expand Up @@ -75,26 +72,29 @@ public interface ClassConfig extends DeclarationConfig {
ClassConfig removeAllAnnotations();

/**
* Returns a collection of {@link MethodConfig} objects for each constructor of this class.
* Returns a collection of {@link MethodConfig} objects for each constructor of this class,
* as defined by {@link ClassInfo#constructors() ClassInfo.constructors}.
*
* @return immutable collection of {@link MethodConfig} objects, never {@code null}
*/
// TODO specify whether inherited constructors are also included; probably mirror what ClassInfo does
Collection<MethodConfig> constructors();

/**
* Returns a collection of {@link MethodConfig} objects for each method of this class.
* Returns a collection of {@link MethodConfig} objects for each method of this class,
* as defined by {@link ClassInfo#methods() ClassInfo.methods}.
*
* @return immutable collection of {@link MethodConfig} objects, never {@code null}
*/
// TODO specify whether inherited methods are also included; probably mirror what ClassInfo does
Collection<MethodConfig> methods();

/**
* Returns a collection of {@link FieldConfig} objects for each field of this class.
* Returns a collection of {@link FieldConfig} objects for each field of this class,
* as defined by {@link ClassInfo#fields() ClassInfo.fields}.
*
* @return immutable collection of {@link FieldConfig} objects, never {@code null}
*/
// TODO specify whether inherited fields are also included; probably mirror what ClassInfo does
Collection<FieldConfig> fields();

// there's no `Collection<RecordComponentConfig> recordComponents()`, because CDI
// doesn't know anything about records and treats them as plain classes
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@
import java.lang.annotation.Target;

/**
* 1st phase of CDI Lite extension execution.
* 1st phase of {@linkplain BuildCompatibleExtension build compatible extension} execution.
* Allow registering additional classes to be scanned during bean discovery.
* Also allows registering custom CDI meta-annotations.
* <p>
* Methods annotated {@code @Discovery} can define parameters of these types:
* Methods annotated {@code @Discovery} may declare parameters of these types:
* <ul>
* <li>{@link ScannedClasses}: to register classes to be scanned during bean discovery</li>
* <li>{@link MetaAnnotations}: to register custom meta-annotations:
* qualifiers, interceptor bindings, stereotypes and scopes</li>
* <li>{@link Messages}: to produce log messages and validation errors</li>
* <li>{@link ScannedClasses}</li>
* <li>{@link MetaAnnotations}</li>
* <li>{@link Messages}</li>
* </ul>
*
* @since 4.0
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,77 @@
package jakarta.enterprise.inject.build.compatible.spi;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 2nd phase of CDI Lite extension execution.
* 2nd phase of {@linkplain BuildCompatibleExtension build compatible extension} execution.
* Allows transforming annotations.
* <p>
* Methods annotated {@code @Enhancement} must define exactly one parameter of one of these types:
* In the following text, the term <em>expected types</em> denotes the set of types defined by
* the {@link #types() types}, {@link #withSubtypes() withSubtypes} and {@link #withAnnotations() withAnnotations}
* members of the {@code @Enhancement} annotation. The term <em>discovered types</em> denotes
* the subset of <em>expected types</em> that were discovered during bean discovery.
* <p>
* Methods annotated {@code @Enhancement} must declare exactly one parameter of one of these types:
* <ul>
* <li>{@link ClassConfig} or {@link jakarta.enterprise.lang.model.declarations.ClassInfo ClassInfo}</li>
* <li>{@link MethodConfig} or {@link jakarta.enterprise.lang.model.declarations.MethodInfo MethodInfo}</li>
* <li>{@link FieldConfig} or {@link jakarta.enterprise.lang.model.declarations.FieldInfo FieldInfo}</li>
* <li>({@code ParameterConfig} or {@code ParameterInfo} is not possible, parameters must be accessed
* through {@code MethodConfig} or {@code MethodInfo})</li>
* </ul>
* The method must also have at least one annotation {@link ExactType @ExactType} or {@link SubtypesOf @SubtypesOf}.
* If an {@code @Enhancement} method has a parameter of type {@code ClassConfig} or {@code ClassInfo},
* the method is called once for each <em>discovered type</em>.
* <p>
* If an {@code @Enhancement} method has a parameter of type {@code MethodConfig} or {@code MethodInfo},
* the method is called once for each constructor or method that is declared on each <em>discovered type</em>,
* as defined in {@link jakarta.enterprise.lang.model.declarations.ClassInfo#constructors() ClassInfo.constructors}
* and {@link jakarta.enterprise.lang.model.declarations.ClassInfo#methods() ClassInfo.methods}.
* <p>
* You can also declare a parameter of type {@link Messages Messages} to produce log messages and validation errors.
* If an {@code @Enhancement} method has a parameter of type {@code FieldConfig} or {@code FieldInfo},
* the method is called once for each field that is declared on each <em>discovered type</em>, as defined
* in {@link jakarta.enterprise.lang.model.declarations.ClassInfo#fields() ClassInfo.fields}.
* <p>
* If you need to create instances of {@link jakarta.enterprise.lang.model.types.Type Type}, you can also declare
* a parameter of type {@link Types}. It provides factory methods for the void pseudo-type, primitive types,
* class types, array types, parameterized types and wildcard types.
* Additionally, methods annotated {@code @Enhancement} may declare parameters of these types:
* <ul>
* <li>{@link Messages}</li>
* <li>{@link Types}</li>
* </ul>
* <p>
* If you need to create instances of {@link jakarta.enterprise.lang.model.AnnotationInfo AnnotationInfo},
* use {@link AnnotationBuilder}.
* Finally, {@link AnnotationBuilder} may be used to create instances
* of {@link jakarta.enterprise.lang.model.AnnotationInfo AnnotationInfo}.
*
* @since 4.0
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Enhancement {
/**
* Defines the set of <em>expected types</em>. If {@link #withSubtypes() withSubtypes}
* is {@code true}, the set of <em>expected types</em> includes all direct and indirect
* subtypes of these types. If {@link #withAnnotations() withAnnotations} is defined,
* the set of <em>expected types</em> only includes types that use given annotations.
*
* @return the set of <em>expected types</em>
*/
Class<?>[] types();

/**
* If {@code true}, the set of <em>expected types</em> includes all direct and
* indirect subtypes of given {@link #types() types}.
*
* @return whether subtypes should be included in the set of <em>expected types</em>
*/
boolean withSubtypes() default false;

/**
* Narrows down the set of <em>expected types</em>, defined by {@link #types() types}
* and {@link #withSubtypes() withSubtypes}, to types that use given annotations.
* The annotations can appear on the type, or on any member of the type,
* or any parameter of any member of the type.
*
* @return types of annotations that must be present on the <em>expected types</em>
*/
Class<? extends Annotation>[] withAnnotations() default Annotation.class;
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public interface InjectionPointInfo {
*
* @return collection of qualifiers, never {@code null}
*/
// TODO method(s) for getting AnnotationInfo for given qualifier class?
Collection<AnnotationInfo> qualifiers();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import jakarta.enterprise.lang.model.AnnotationTarget;

/**
* Allows logging and producing errors during {@link BuildCompatibleExtension} execution.
* If an error is produced, application deployment will fail.
* Allows logging and producing errors during {@linkplain BuildCompatibleExtension build compatible extension}
* execution. If an error is produced, using any of the {@code error} methods, the container treats it
* as a deployment problem.
*
* @since 4.0
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,39 +13,47 @@
* @since 4.0
*/
public interface MetaAnnotations {
// TODO this API style is not very common, and makes addContext too different
// I only added it to make Quarkus implementation easier, but should definitely be reconsidered

/**
* Registers {@code annotation} as a qualifier annotation. Only makes sense if the annotation
* is not meta-annotated {@code @Qualifier}.
* <p>
* Returns a {@linkplain ClassConfig class configurator} object that allows transforming meta-annotations
* on the {@code annotation}.
*
* @param annotation annotation type
* @param config allows transforming annotations on the {@code annotation}
* @return the {@linkplain ClassConfig class configurator}, never {@code null}
*/
void addQualifier(Class<? extends Annotation> annotation, Consumer<ClassConfig> config);
ClassConfig addQualifier(Class<? extends Annotation> annotation);

/**
* Registers {@code annotation} as an interceptor binding annotation. Only makes sense if the annotation
* is not meta-annotated {@code @InterceptorBinding}.
* <p>
* Returns a {@linkplain ClassConfig class configurator} object that allows transforming meta-annotations
* on the {@code annotation}.
*
* @param annotation annotation type
* @param config allows transforming annotations on the {@code annotation}
* @return the {@linkplain ClassConfig class configurator}, never {@code null}
*/
void addInterceptorBinding(Class<? extends Annotation> annotation, Consumer<ClassConfig> config);
ClassConfig addInterceptorBinding(Class<? extends Annotation> annotation);

/**
* Registers {@code annotation} as a stereotype annotation. Only makes sense if the annotation
* is not meta-annotated {@code @Stereotype}.
* <p>
* Returns a {@linkplain ClassConfig class configurator} object that allows transforming meta-annotations
* on the {@code annotation}.
*
* @param annotation annotation type
* @param config allows transforming annotations on the {@code annotation}
* @return the {@linkplain ClassConfig class configurator}, never {@code null}
*/
void addStereotype(Class<? extends Annotation> annotation, Consumer<ClassConfig> config);
ClassConfig addStereotype(Class<? extends Annotation> annotation);

/**
* Registers custom context for given {@code scopeAnnotation} and given {@code contextClass}.
* The context class must be {@code public} and have a {@code public} zero-parameter constructor.
* CDI container will create an instance of the context class once to obtain the context object.
* The context class must be {@code public} and have a {@code public} zero-parameter constructor;
* it must not be a bean.
* <p>
* Whether the scope is normal is discovered from the scope annotation. This means that the scope
* annotation must be meta-annotated either {@link jakarta.enterprise.context.NormalScope @NormalScope}
Expand All @@ -60,7 +68,9 @@ public interface MetaAnnotations {

/**
* Registers custom context for given {@code scopeAnnotation} and given {@code contextClass}.
* The context class must be {@code public} and have a {@code public} zero-parameter constructor.
* CDI container will create an instance of the context class once to obtain the context object.
* The context class must be {@code public} and have a {@code public} zero-parameter constructor;
* it must not be a bean.
* <p>
* The {@code isNormal} parameter determines whether the scope is a normal scope or a pseudo-scope.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ public interface ObserverInfo {
*
* @return immutable collection of observed event qualifiers, never {@code null}
*/
// TODO method(s) for getting AnnotationInfo for given qualifier class?
Collection<AnnotationInfo> qualifiers();

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

/**
* A {@code String}-keyed parameter map. The parameter mappings are defined
* by a synthetic component builder. The CDI container passes the map
* by a synthetic component builder. The CDI container passes the parameter map
* to functions defined by the same synthetic component builder, whenever
* it needs to invoke those functions. That is:
*
Expand Down

0 comments on commit 93dc036

Please sign in to comment.