Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve extension API and resolve all remaining open questions #544

Merged
merged 1 commit into from
Oct 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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,38 @@
/**
* 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.
* If two extension methods have equal priority, the ordering between them is undefined. Note that priority
* 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}
* When extension methods are invoked, a CDI container does not have to be running, so calling {@code CDI.current()}
* from an extension method, or attempting to access a running CDI container in any other way, results in
* non-portable behavior.
* <p>
* 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 +44,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,92 @@
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 any of given annotations.
* The annotation can appear on the type, or on any member of the type, or on any
* parameter of any member of the type, or as a meta-annotation on any annotation
* that is considered by these rules.
* <p>
* Defaults to the {@linkplain BeanDefiningAnnotations set of bean defining annotations}.
* <p>
* If empty, or if {@code java.lang.Annotation} is present, all annotations are used.
* That is, the set of <em>expected types</em> is narrowed down to the set of types
* that use any annotation.
*
* @return types of annotations that must be present on the <em>expected types</em>
*/
Class<? extends Annotation>[] withAnnotations() default BeanDefiningAnnotations.class;

/**
* Marker annotation type that represents set of bean defining annotations after
* the {@link Discovery @Discovery} phase is finished. That is, it includes custom
* normal scope annotations as well as custom stereotypes.
*/
@interface BeanDefiningAnnotations {
}
}

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