diff --git a/api/src/main/java/jakarta/enterprise/inject/build/compatible/spi/BuildCompatibleExtension.java b/api/src/main/java/jakarta/enterprise/inject/build/compatible/spi/BuildCompatibleExtension.java index 0cb55a54..d334a7aa 100644 --- a/api/src/main/java/jakarta/enterprise/inject/build/compatible/spi/BuildCompatibleExtension.java +++ b/api/src/main/java/jakarta/enterprise/inject/build/compatible/spi/BuildCompatibleExtension.java @@ -18,9 +18,10 @@ *
  • {@link Validation @Validation}
  • * *

    - * 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. + * Extension methods may declare arbitrary number of parameters. In each execution phase, different types + * of parameters may be declared, as documented in the corresponding extension annotation. All the parameters + * will be provided by the container when the extension method is invoked. If an extension method declares + * a parameter of a type unsupported in the execution phase, the container treats it as a deployment problem. *

    * 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 @@ -28,7 +29,8 @@ * 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. *

    - * If the extension declares multiple extension methods, they are all invoked on the same instance of the class. + * For each build compatible extension, the container creates a single instance. All extension methods + * are invoked on the same instance. *

    * 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 diff --git a/api/src/main/java/jakarta/enterprise/inject/build/compatible/spi/package-info.java b/api/src/main/java/jakarta/enterprise/inject/build/compatible/spi/package-info.java new file mode 100644 index 00000000..30d5c28c --- /dev/null +++ b/api/src/main/java/jakarta/enterprise/inject/build/compatible/spi/package-info.java @@ -0,0 +1,13 @@ +/** + *

    The build compatible extension SPI. See:

    + * + * + */ +package jakarta.enterprise.inject.build.compatible.spi; diff --git a/spec/src/main/asciidoc/core/packagingdeployment.asciidoc b/spec/src/main/asciidoc/core/packagingdeployment.asciidoc index 7dce658a..b07ef542 100644 --- a/spec/src/main/asciidoc/core/packagingdeployment.asciidoc +++ b/spec/src/main/asciidoc/core/packagingdeployment.asciidoc @@ -2,7 +2,8 @@ == Packaging and deployment -Before an application is started, or during application startup, the container must perform _bean discovery_, execute build compatible extensions, and detect definition errors and deployment problems. +At deployment time, the container must perform _bean discovery_, execute <>, and detect definition errors and deployment problems. +The term _deployment time_ in {cdi_lite} means before the application is started, such as during application compilation, or during application startup at latest. Bean discovery is the process of determining: @@ -10,7 +11,7 @@ Bean discovery is the process of determining: * Which alternatives and interceptors are _enabled_ * The _ordering_ of enabled interceptors -Additional beans may be registered programmatically via <>. +Additional beans may be registered programmatically using build compatible extensions. [[bean_archive]] @@ -34,8 +35,11 @@ An _implicit bean archive_ is: * an archive which contains a `beans.xml` file that is empty, or, * an archive which contains a `beans.xml` file that has `bean-discovery-mode` attribute set to `annotated` -When determining which archives are bean archives, the container must consider all JAR archives available through implementation-specific means. -Implementations are required to document how the candidate JAR archives are found. +Any other archive which contains a `beans.xml` file is not portable in {cdi_lite}. +More kinds of bean archives exist in {cdi_full}. + +When determining which archives are bean archives, the container must consider all archives that constitute the application. +Implementations are encouraged to document how the candidate archives are found in more detail. The `beans.xml` file must be named: @@ -50,14 +54,27 @@ Implicit bean archives are likely to contain classes which are not deployed as b An extension may be deployed in any archive, including those that are not bean archives. +[[deployment]] + +=== Deployment + +At deployment time, the container performs the following steps: + +* First, the container must perform type discovery, as defined in <>. + As part of that, the container must execute the <> and <> phases of build compatible extensions. +* Next, the container must perform bean discovery, as defined in <>. + As part of that, the container must execute the <> and <> phases of build compatible extensions. +* Finally, the container must detect deployment problems by validating bean dependencies. + As part of that, the container must execute the <> phase of build compatible extensions. + +At any step, the container must abort deployment if any definition errors or deployment problems exist, as defined in <>. + [[initialization]] === Application initialization lifecycle -// TODO related to https://github.com/eclipse-ee4j/cdi/issues/482 -// also related to https://github.com/eclipse-ee4j/cdi/issues/496 -// Capture init process WRT build compatible extensions + type discovery + bean discovery -// see <> for format that is used for CDI Full +{cdi_lite} does not require the container to perform any other initialization during application startup. +With deployment complete, the container begins directing requests to the application. [[shutdown]] @@ -78,9 +95,9 @@ The container automatically discovers managed beans (according to the rules of < First the container must discover types. The container discovers each Java class with a bean defining annotation in an implicit bean archive. -// TODO at this point, we should probably mention build compatible extensions and how you can get notified of events and change these types -// we should mention Enhancement phase here and link to the relevant doc part -// related to https://github.com/eclipse-ee4j/cdi/issues/483 +The container must also execute the `@Discovery` phase of build compatible extensions and discover all classes added using the `ScannedClasses` API. + +When all types are discovered, the container must execute the `@Enhancement` phase of build compatible extensions and alter its metadata representation of discovered types accordingly. [[bean_discovery_steps]] @@ -91,24 +108,19 @@ For every type in the set of discovered types (as defined in <>. -// TODO mention any events we fire for build compatible extensions at this point - Processing -// e.g. if we have an equivalent of ProcessBeanAttributes and/or ProcessBean -// related to https://github.com/eclipse-ee4j/cdi/issues/483 -For each enabled bean, the container must search the class for producer methods and fields, as defined in <> and in <>, including resources, and for each producer: - -// TODO mention any events we fire for build compatible extensions at this point - Processing -// e.g. if we have an equivalent of ProcessInjectionPoint and/or ProcessProducer and/or ProcessBeanAttributes and/or ProcessBean -// related to https://github.com/eclipse-ee4j/cdi/issues/483 - -For each enabled bean, the container must search the class for observer methods, and for each observer method: - -// TODO mention build compatible extension equivalent - Processing -// e.g. if we have an equivalent of ProcessObserverMethod -// related to https://github.com/eclipse-ee4j/cdi/issues/483 +For each enabled bean, the container must search the class for producer methods and fields, as defined in <> and in <>, including resources, and for disposer methods as defined in <>, and for observer methods. Then, the container registers the `Bean` and `ObserverMethod` objects: * For each enabled bean that is not an interceptor, the container registers an instance of the `Bean` interface defined in <>. * For each enabled interceptor, the container registers an instance of the `Interceptor` interface defined in <>. * For each observer method of every enabled bean, the container registers an instance of the `ObserverMethod` interface defined in <>. + +The container must execute the `@Registration` phase of build compatible extensions for each registered bean, interceptor, and observer method. + +Next, the container must execute the `@Synthesis` phase of build compatible extensions. +For each registered synthetic bean, the container registers an instance of the `Bean` interface. +For each registered synthetic observer, the container registers an instance of the `ObserverMethod` interface. + +Finally, the container must execute the `@Registration` phase of build compatible extensions for each synthetic bean and synthetic observer method. diff --git a/spec/src/main/asciidoc/core/packagingdeployment_full.asciidoc b/spec/src/main/asciidoc/core/packagingdeployment_full.asciidoc index 798e0cf4..2f91a043 100644 --- a/spec/src/main/asciidoc/core/packagingdeployment_full.asciidoc +++ b/spec/src/main/asciidoc/core/packagingdeployment_full.asciidoc @@ -3,7 +3,8 @@ == Packaging and deployment in {cdi_full} This chapter replaces <> for the purpose of {cdi_full}. -In case of overlap, the <> chapter should be considered merely informative. +The <> chapter should be considered merely informative. +In {cdi_full}, the term _deployment time_ always means during application startup. When an application is started, the container must perform _bean discovery_, detect definition errors and deployment problems and raise events that allow portable extensions to integrate with the deployment lifecycle. @@ -108,7 +109,7 @@ First the container must discover types. The container discovers: * each Java class, interface (excluding the special kind of interface declaration _annotation type_) or enum deployed in an explicit bean archive, and -* each Java class with a bean defining annotation in an implicit bean archive. +* each Java class with a bean defining annotation in an implicit bean archive, that is not excluded from discovery by an _exclude filter_ as defined in <>. diff --git a/spec/src/main/asciidoc/core/spi_full.asciidoc b/spec/src/main/asciidoc/core/spi_full.asciidoc index 7457cba5..fccaeab5 100644 --- a/spec/src/main/asciidoc/core/spi_full.asciidoc +++ b/spec/src/main/asciidoc/core/spi_full.asciidoc @@ -884,6 +884,10 @@ Lifecycle events described below can be grouped into two categories: Note that the chronological order of these events is specified in <>. +As these lifecycle events are fired, the container must also execute <>. +Which phase of build compatible extensions should be executed when is indicated in the description of the corresponding lifecycle events. +Build compatible extensions annotated `@SkipIfPortableExtensionPresent` must be ignored in {cdi_full}, if given portable extension is present. + [[before_bean_discovery]] ==== `BeforeBeanDiscovery` event @@ -928,6 +932,7 @@ If any observer method of the `BeforeBeanDiscovery` event throws an exception, t If any `BeforeBeanDiscovery` method is called outside of the observer method invocation, an `IllegalStateException` is thrown. +The container must execute the `@Discovery` phase of build compatible extensions at this time. [[after_type_discovery]] @@ -1026,6 +1031,7 @@ If any observer method of the `AfterBeanDiscovery` event throws an exception, th If any `AfterBeanDiscovery` method is called outside of the observer method invocation, an `IllegalStateException` is thrown. +The container must execute the `@Synthesis` phase of build compatible extensions at this time. [[bean_configurator]] @@ -1096,6 +1102,8 @@ If any `AfterDeploymentValidation` method is called outside of the observer meth The container must not allow any request to be processed by the deployment until all observers of this event return. +The container must execute the `@Validation` phase of build compatible extensions at this time. + [[before_shutdown]] ==== `BeforeShutdown` event @@ -1175,6 +1183,8 @@ If any observer method of a `ProcessAnnotatedType` event throws an exception, th If any `ProcessAnnotatedType` method is called outside of the observer method invocation, an `IllegalStateException` is thrown. +The container must execute the `@Enhancement` phase of build compatible extensions at this time. + [[process_injection_point]] ==== `ProcessInjectionPoint` event @@ -1397,6 +1407,8 @@ If any observer method of a `ProcessBean` event throws an exception, the excepti If any `ProcessBean` method is called outside of the observer method invocation, an `IllegalStateException` is thrown. +The container must execute the bean-related part of `@Registration` phase of build compatible extensions at this time. + [[process_producer]] ==== `ProcessProducer` event @@ -1497,6 +1509,8 @@ If any observer method of a `ProcessObserverMethod` event throws an exception, t If any `ProcessObserverMethod` method is called outside of the observer method invocation, an `IllegalStateException` is thrown. +The container must execute the observer-related part of `@Registration` phase of build compatible extensions at this time. + [[configurators]] === Configurators interfaces diff --git a/spec/src/main/asciidoc/core/spi_lite.asciidoc b/spec/src/main/asciidoc/core/spi_lite.asciidoc index bf1f4421..f5d33204 100644 --- a/spec/src/main/asciidoc/core/spi_lite.asciidoc +++ b/spec/src/main/asciidoc/core/spi_lite.asciidoc @@ -2,11 +2,387 @@ == Build compatible extensions -// TODO -[NOTE] -==== -TBD -==== - -// Must mention that Build Compatible Extensions do not have access to the CDI container, -// as there may not be one running! Calling `CDI.current()` inside them results in non-portable behavior. +A build compatible extension may integrate with the container during deployment time, as defined in <>. + +[[bce]] + +=== The `BuildCompatibleExtension` interface + +A build compatible extension is a service provider of the `jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension` interface, declared in `META-INF/services`. + +[source, java] +---- +public interface BuildCompatibleExtension {} +---- + +Build compatible extensions can define arbitrary `public`, non-`static`, `void`-returning methods without type parameters, annotated with one of the extension annotations. +Such methods are called _extension methods_. + +Extension annotations correspond to extension execution phases: + +* `@Discovery` +* `@Enhancement` +* `@Registration` +* `@Synthesis` +* `@Validation` + +Extension methods may declare arbitrary number of parameters. +In each execution phase, different types of parameters may be declared. +All the parameters will be provided by the container when the extension method is invoked. +If an extension method declares a parameter of a type unsupported in the execution phase, the container treats it as a deployment problem. + +For each build compatible extension, the container creates a single instance. +All extension methods are invoked on the same instance. + +The invocation order for extension methods may be controlled using the `@Priority` annotation. +If an extension method does not have the `@Priority` annotation, the default priority `jakarta.interceptor.Interceptor.Priority.APPLICATION + 500` is assumed. +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. + +If an extension method throws an exception, the container treats it as a deployment problem. + +At deployment time, CDI container does not have to be running, so calling `CDI.current()` from an extension method, or attempting to access a running CDI container in any other way, results in non-portable behavior. + +[[bce_discovery]] + +=== The `@Discovery` phase + +In this phase, build compatible extensions may register additional classes to be scanned during type discovery, and register custom CDI meta-annotations. + +Extension methods annotated `@Discovery` may declare parameters of the following types: + +* `ScannedClasses` +* `MetaAnnotations` +* `Messages` (see <>) + +[source, java] +---- +public interface ScannedClasses { + void add(String className); +} +---- + +[source, java] +---- +public interface MetaAnnotations { + ClassConfig addQualifier(Class annotation); + ClassConfig addInterceptorBinding(Class annotation); + ClassConfig addStereotype(Class annotation); + + void addContext(Class scopeAnnotation, Class contextClass); + void addContext(Class scopeAnnotation, boolean isNormal, Class contextClass); +} +---- + +If the `addQualifier`, `addInterceptorBinding` or `addStereotype` method is called, the return value allows configuring meta-annotations on the qualifier, interceptor binding or stereotype annotation and its members. + +[[bce_enhancement]] + +=== The `@Enhancement` phase + +In this phase, build compatible extensions may alter annotations on discovered types. + +Extension methods annotated `@Enhancement` must declare exactly one parameter of one of the following types: + +* `ClassConfig` or `ClassInfo` +* `MethodConfig` or `MethodInfo` +* `FieldConfig` or `FieldInfo` + +[source, java] +---- +public interface ClassConfig extends DeclarationConfig { + ClassInfo info(); + + ClassConfig addAnnotation(Class annotationType); + ClassConfig addAnnotation(AnnotationInfo annotation); + ClassConfig addAnnotation(Annotation annotation); + ClassConfig removeAnnotation(Predicate predicate); + ClassConfig removeAllAnnotations(); + + Collection constructors(); + Collection methods(); + Collection fields(); +} + +public interface ClassInfo extends DeclarationInfo { + String name(); + String simpleName(); + PackageInfo packageInfo(); + List typeParameters(); + + Type superClass(); + ClassInfo superClassDeclaration(); + List superInterfaces(); + List superInterfacesDeclarations(); + + boolean isPlainClass(); + boolean isInterface(); + boolean isEnum(); + boolean isAnnotation(); + boolean isRecord(); + boolean isAbstract(); + boolean isFinal(); + int modifiers(); + + Collection constructors(); + Collection methods(); + Collection fields(); + Collection recordComponents(); +} +---- + +When an extension method declares a parameter of type `ClassConfig` or `ClassInfo`, it will be called for each discovered class matching the criteria defined on the `@Enhancement` annotation. +It is possible to navigate to constructors, methods and fields from a `ClassConfig` and configure them. + +[source, java] +---- +public interface MethodConfig extends DeclarationConfig { + MethodInfo info(); + + MethodConfig addAnnotation(Class annotationType); + MethodConfig addAnnotation(AnnotationInfo annotation); + MethodConfig addAnnotation(Annotation annotation); + MethodConfig removeAnnotation(Predicate predicate); + MethodConfig removeAllAnnotations(); + + List parameters(); +} + +public interface ParameterConfig extends DeclarationConfig { + ParameterInfo info(); + + ParameterConfig addAnnotation(Class annotationType); + ParameterConfig addAnnotation(AnnotationInfo annotation); + ParameterConfig addAnnotation(Annotation annotation); + ParameterConfig removeAnnotation(Predicate predicate); + ParameterConfig removeAllAnnotations(); +} + +public interface MethodInfo extends DeclarationInfo { + String name(); + List parameters(); + Type returnType(); + Type receiverType(); + List throwsTypes(); + List typeParameters(); + + boolean isConstructor(); + boolean isStatic(); + boolean isAbstract(); + boolean isFinal(); + int modifiers(); + + ClassInfo declaringClass(); +} + +public interface ParameterInfo extends DeclarationInfo { + String name(); + Type type(); + + MethodInfo declaringMethod(); +} +---- + +When an extension method declares a parameter of type `MethodConfig` or `MethodInfo`, it will be called for each method and constructor of each discovered class matching the criteria defined on the `@Enhancement` annotation. +It is possible to navigate to method parameters from a `MethodConfig` and configure them. + +[source, java] +---- +public interface FieldConfig extends DeclarationConfig { + FieldInfo info(); + + FieldConfig addAnnotation(Class annotationType); + FieldConfig addAnnotation(AnnotationInfo annotation); + FieldConfig addAnnotation(Annotation annotation); + FieldConfig removeAnnotation(Predicate predicate); + FieldConfig removeAllAnnotations(); +} + +public interface FieldInfo extends DeclarationInfo { + String name(); + Type type(); + + boolean isStatic(); + boolean isFinal(); + int modifiers(); + + ClassInfo declaringClass(); +} +---- + +When an extension method declares a parameter of type `FieldConfig` or `FieldInfo`, it will be called for each field of each discovered class matching the criteria defined on the `@Enhancement` annotation. + +Additionally, extension methods annotated `@Enhancement` may declare parameters of the following types: + +* `Types` +* `Messages` (see <>) + +[source, java] +---- +public interface Types { + Type of(Class clazz); + VoidType ofVoid(); + PrimitiveType ofPrimitive(PrimitiveType.PrimitiveKind kind); + ClassType ofClass(String name); + ClassType ofClass(ClassInfo clazz); + ArrayType ofArray(Type elementType, int dimensions); + ParameterizedType parameterized(Class genericType, Class... typeArguments); + ParameterizedType parameterized(Class genericType, Type... typeArguments); + ParameterizedType parameterized(ClassType genericType, Type... typeArguments); + WildcardType wildcardWithUpperBound(Type upperBound); + WildcardType wildcardWithLowerBound(Type lowerBound); + WildcardType wildcardUnbounded(); +} +---- + +The `Types` interface allows creating representations of the void pseudo-type, primitive types, class types, array types, parameterized types and wildcard types. + +To create instances of `AnnotationInfo`, `AnnotationBuilder` can be used. + +[[bce_registration]] + +=== The `@Registration` phase + +In this phase, build compatible extensions may observe registered beans and observers. + +Extension methods annotated `@Registration` must declare exactly one parameter of one of the following types: + +* `BeanInfo` +* `InterceptorInfo` +* `ObserverInfo` + +[source, java] +---- +public interface BeanInfo { + ScopeInfo scope(); + Collection types(); + Collection qualifiers(); + ClassInfo declaringClass(); + boolean isClassBean(); + boolean isProducerMethod(); + boolean isProducerField(); + boolean isSynthetic(); + MethodInfo producerMethod(); + FieldInfo producerField(); + boolean isAlternative(); + Integer priority(); + String getName(); + DisposerInfo disposer(); + Collection stereotypes(); + Collection injectionPoints(); +} +---- + +When an extension method declares a parameter of type `BeanInfo`, it will be called for each bean whose set of bean types matches the criteria defined on the `@Registration` annotation. + +[source, java] +---- +public interface InterceptorInfo extends BeanInfo { + Collection interceptorBindings(); + boolean intercepts(InterceptionType interceptionType); +} +---- + +When an extension method declares a parameter of type `InterceptorInfo`, it will be called for each interceptor whose set of bean types matches the criteria defined on the `@Registration` annotation. + +[source, java] +---- +public interface ObserverInfo { + Type observedType(); + Collection qualifiers(); + ClassInfo declaringClass(); + MethodInfo observerMethod(); + ParameterInfo eventParameter(); + BeanInfo bean(); + boolean isSynthetic(); + int priority(); + boolean isAsync(); + Reception reception(); + TransactionPhase transactionPhase(); +} +---- + +When an extension method declares a parameter of type `ObserverInfo`, it will be called for each observer whose observed event type matches the criteria defined on the `@Registration` annotation. + +Additionally, extension methods annotated `@Registration` may declare parameters of the following types: + +* `Types` +* `Messages` (see <>) + +[[bce_synthesis]] + +=== The `@Synthesis` phase + +In this phase, build compatible extensions may register synthetic beans and observers. + +Extension methods annotated `@Synthesis` may declare parameters of the following types: + +* `SyntheticComponents` +* `Types` +* `Messages` (see <>) + +[source, java] +---- +public interface SyntheticComponents { + SyntheticBeanBuilder addBean(Class beanClass); + SyntheticObserverBuilder addObserver(Class eventType); +} +---- + +The `SyntheticBeanBuilder` and `SyntheticObserverBuilder` interfaces are used to configure: + +* bean or observer attributes, such as scope, bean types, qualifiers, or observed event type; +* class of a bean creation/destruction function or observer notification function; +* a string-keyed parameter map. + +The container creates an instance of the bean creation/destruction function or observer notification function whenever it needs to create an instance of the bean, destroy the instance of the bean, or notify the observer. +When invoking the bean creation/destruction function or observer notification function, the container passes the parameter map to it. + +The parameter map may contain values of the following types: + +* `boolean` +* `int` +* `long` +* `double` +* `String` +* `Class` +* `Enum` +* any annotation type +* array of any previously mentioned type + +When defining the parameter map on `SyntheticBeanBuilder` or `SyntheticObserverBuilder`, it is possible to use `ClassInfo` or `AnnotationInfo` to define parameter values. +When such parameter is looked up from the parameter map in the synthetic bean creation/destruction function or the synthetic observer notification function, the value will be of type `Class` or the respective annotation type. + +[[bce_validation]] + +=== The `@Validation` phase + +In this phase, build compatible extensions may perform custom validation. + +Extension methods annotated `@Validation` may declare parameters of the following types: + +* `Types` +* `Messages` + +[source, java] +---- +public interface Messages { + void info(String message); + void info(String message, AnnotationTarget relatedTo); + void info(String message, BeanInfo relatedTo); + void info(String message, ObserverInfo relatedTo); + + void warn(String message); + void warn(String message, AnnotationTarget relatedTo); + void warn(String message, BeanInfo relatedTo); + void warn(String message, ObserverInfo relatedTo); + + void error(String message); + void error(String message, AnnotationTarget relatedTo); + void error(String message, BeanInfo relatedTo); + void error(String message, ObserverInfo relatedTo); + void error(Exception exception); +} +---- + +Calling any of the `Messages.error()` methods registers a deployment problem.