diff --git a/.github/workflows/ci-actions.yml b/.github/workflows/ci-actions.yml index f9bc89125f7..e2848c14345 100644 --- a/.github/workflows/ci-actions.yml +++ b/.github/workflows/ci-actions.yml @@ -35,7 +35,7 @@ jobs: - name: Get Date id: get-date run: | - echo "::set-output name=date::$(/bin/date -u "+%Y-%m")" + echo "date=$(/bin/date -u '+%Y-%m')" >> "$GITHUB_OUTPUT" shell: bash - name: Cache Maven Repository id: cache-maven @@ -360,7 +360,7 @@ jobs: # Weld Examples build and test, only JDK 17 examples-tests: - name: "Weld Examples build and test - JDK 17}" + name: "Weld Examples build and test - JDK 17" runs-on: ubuntu-latest needs: initial-build timeout-minutes: 120 @@ -416,7 +416,7 @@ jobs: if: failure() with: name: server-log-examples - path: 'test-reports.tgz' + path: 'server-log.tgz' # CDI TCK for SE environment CDI-TCK-SE: diff --git a/README.md b/README.md index 7304316cf0c..a408fd9eb0b 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,16 @@ Weld [![Maven Central](http://img.shields.io/maven-central/v/org.jboss.weld.se/weld-se-shaded.svg)](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22weld-core-impl%22) [![License](https://img.shields.io/badge/license-Apache%20License%202.0-yellow.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) -Weld is the reference implementation of CDI: Contexts and Dependency Injection for the Java EE Platform which is the Java standard for dependency injection and contextual lifecycle management and one of the most important and popular parts of the Java EE platform. +Weld is the reference implementation of Jakarta Contexts and Dependency Injection (CDI), the Java standard for dependency injection and contextual lifecycle management. -Weld is integrated into many Java EE application servers such as WildFly, JBoss Enterprise Application Platform, GlassFish, Oracle WebLogic and others. Weld can also be used in a Servlet-only environment (Tomcat, Jetty) or plain Java SE environment. +Weld can run in Jakarta EE application servers, Servlet containers (such as Tomcat and Jetty), and plain Java SE applications. See http://weld.cdi-spec.org for more details. Building Weld ------------- -To build Weld simply run +To build Weld, use Java 17 or newer and run > $ mvn clean install diff --git a/bom/pom.xml b/bom/pom.xml index 08d71359e0e..ab42268b872 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -30,9 +30,8 @@ - 7.0.CR1 + 7.0.CR2 3.2.8 - 1.7.0 https://repository.jboss.org/nexus/service/local/staging/deploy/maven2/ https://repository.jboss.org/nexus/content/repositories/snapshots/ diff --git a/bundles/osgi/pom.xml b/bundles/osgi/pom.xml index 247652246db..34c4a3f12c5 100644 --- a/bundles/osgi/pom.xml +++ b/bundles/osgi/pom.xml @@ -371,7 +371,7 @@ scm:git:git@github.com:weld/core.git scm:git:git@github.com:weld/core.git scm:git:git@github.com:weld/core.git - 6.0.0-SNAPSHOT + HEAD diff --git a/docs/reference/src/main/asciidoc/configure.asciidoc b/docs/reference/src/main/asciidoc/configure.asciidoc index e8fe45c1e61..7001e10cea6 100644 --- a/docs/reference/src/main/asciidoc/configure.asciidoc +++ b/docs/reference/src/main/asciidoc/configure.asciidoc @@ -294,7 +294,7 @@ The following properties can be used to restrict the set of beans Weld is going |======================================================================= |Configuration key |Default value |Description |`org.jboss.weld.bootstrap.unusedBeans.excludeType` |`ALL`| A regular expression. A bean whose `Bean#getBeanClass()` matches this pattern is never removed. Two special values are considered. `ALL` (default value) means that all beans are excluded. `NONE` means no beans are excluded. -|`org.jboss.weld.bootstrap.unusedBeans.excludeAnnotation` |`javax\\.ws\\.rs.*`| A regular expression. A bean is not removed if the corresponding `AnnotatedType`, or any member, is annotated with an annotation which matches this pattern. By default, a type annotated with any JAX-RS annotation is excluded from removal. +|`org.jboss.weld.bootstrap.unusedBeans.excludeAnnotation` |`jakarta\\.ws\\.rs.*`| A regular expression. A bean is not removed if the corresponding `AnnotatedType`, or any member, is annotated with an annotation which matches this pattern. By default, a type annotated with any JAX-RS annotation is excluded from removal. |======================================================================= [[legacy-empty-beans-xml]] @@ -530,3 +530,25 @@ xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ ----------------------------------------------------------------------------------------------------------- + +[[async_handler_selection]] +=== Selecting asynchronous invocation handlers + +CDI provides asynchronous invocation handlers for `CompletionStage`, `CompletableFuture`, and `Flow.Publisher`. +Additional handlers are discovered through the `AsyncHandler.ReturnType` and `AsyncHandler.ParameterType` service-provider files. +If more than one handler is registered for the same async type, including a collision with a built-in handler, deployment fails by default. + +Use `org.jboss.weld.invokable.asyncHandlers` to select a handler explicitly. +Its value is a comma-separated list of `asyncType=handlerClass` mappings using binary class names (`$` for nested classes). +For example, in `weld.properties`: + +[source,properties] +---- +org.jboss.weld.invokable.asyncHandlers=java.util.concurrent.CompletionStage=com.example.MyStageHandler +---- + +The selected handler must be registered through its service-provider file (or be a built-in handler) and declare the configured async type. +Other handlers for that type are ignored when selecting the handler. +An absent provider, a mismatched async type, a malformed mapping, or repeated mappings for the same async type cause deployment to fail. +The default value is an empty string, meaning that no explicit selections are made. +As with other Weld configuration properties, the value may also be supplied through a system property or external configuration. diff --git a/docs/reference/src/main/asciidoc/environments.asciidoc b/docs/reference/src/main/asciidoc/environments.asciidoc index f7ce57a6ad2..a82dc2b8a4b 100644 --- a/docs/reference/src/main/asciidoc/environments.asciidoc +++ b/docs/reference/src/main/asciidoc/environments.asciidoc @@ -266,7 +266,7 @@ While this works most of the time, there are few rare cases, when it might be ne * `org.jboss.weld.environment.tomcat.TomcatContainer` * `org.jboss.weld.environment.jetty.JettyContainer` * `org.jboss.weld.environment.undertow.UndertowContainer` -* or any custom `org.jboss.weld.environment.Container` implementation +* or any custom `org.jboss.weld.environment.servlet.Container` implementation [[weld-se]] diff --git a/docs/reference/src/main/asciidoc/extend.asciidoc b/docs/reference/src/main/asciidoc/extend.asciidoc index 3066a1b88ca..d60b0cab9ea 100644 --- a/docs/reference/src/main/asciidoc/extend.asciidoc +++ b/docs/reference/src/main/asciidoc/extend.asciidoc @@ -277,10 +277,10 @@ AnnotatedType type = beanManager.createAnnotatedType(Som //The extension uses an InjectionTarget to delegate instantiation, dependency injection //and lifecycle callbacks to the CDI container -InjectionTarget it = beanManager.createInjectionTarget(type); +InjectionTarget it = beanManager.getInjectionTargetFactory(type).createInjectionTarget(null); //each instance needs its own CDI CreationalContext -CreationalContext ctx = beanManager.createCreationalContext(null); +CreationalContext ctx = beanManager.createCreationalContext(null); //instantiate the framework component and inject its dependencies SomeFrameworkComponent instance = it.produce(ctx); //call the constructor @@ -317,6 +317,9 @@ public interface BeanAttributes { public String getName(); public Set> getStereotypes(); public boolean isAlternative(); + public default boolean isReserve() { return false; } + public default boolean isEager() { return false; } + public default boolean isAutoClose() { return false; } } ------------------------------------------------------------ @@ -328,7 +331,6 @@ everything the container needs to manage instances of a certain bean. public interface Bean extends Contextual, BeanAttributes { public Class getBeanClass(); public Set getInjectionPoints(); - public boolean isNullable(); } ------------------------------------------------------------------- diff --git a/docs/reference/src/main/asciidoc/injection.asciidoc b/docs/reference/src/main/asciidoc/injection.asciidoc index 2d608e7fee0..4a0a2e50331 100644 --- a/docs/reference/src/main/asciidoc/injection.asciidoc +++ b/docs/reference/src/main/asciidoc/injection.asciidoc @@ -575,61 +575,36 @@ Since CDI 2.0, most annotations from `jakarta.enterprise` package have their `An Therefore, in order to programmatically obtain (for instance) `@Any` annotation, you can simply do `Any.Literal.INSTANCE`. ==== -==== Enhanced version of `jakarta.enterprise.inject.Instance` - -Weld also provides `org.jboss.weld.inject.WeldInstance` - an enhanced version of `jakarta.enterprise.inject.Instance`. -There are three additional methods. -The first one - `getHandler()` - allows to obtain a contextual reference handler which not only holds the contextual reference but also allows to inspect the metadata of the relevant bean and to destroy the underlying contextual instance. -Moreover, the handler implements `AutoCloseable`: +`Instance.getHandle()` returns an `Instance.Handle`, which provides access to the contextual reference through `get()`, bean metadata through `getBean()`, and explicit destruction through `destroy()`. +Handles also implement `AutoCloseable` and can be used in a try-with-resources statement: [source.JAVA, java] ---------------------------------------------------------------------------------- -import org.jboss.weld.inject.WeldInstance; +import jakarta.enterprise.inject.Instance; class Foo { @Inject - WeldInstance instance; + Instance instance; void doWork() { - try (Handler barHandler = instance.getHandler()) { - barHandler.get().doBusiness(); - // Note that Bar will be automatically destroyed at the end of the try-with-resources statement + try (Instance.Handle handle = instance.getHandle()) { + handle.get().doBusiness(); } - Handler barHandler = instance.getHandler() - barHandler.get().doBusiness(); - // Calls Instance.destroy() - barHandler.destroy(); } } ---------------------------------------------------------------------------------- -The next method - `handlers()` - returns an `Iterable` which allows to iterate over handlers for all the beans that have the required type and required qualifiers and are eligible for injection. -This might be useful if you need more control inside the loop: +`Instance.handles()` and `Instance.handlesStream()` provide iteration and streaming over handles for all matching beans. +These are standard CDI APIs and do not require a dependency on the Weld API. -[source.JAVA, java] ----------------------------------------------------------------------------------- -@ApplicationScoped -class OrderService { +==== Enhanced version of `jakarta.enterprise.inject.Instance` - @Inject - @Any - WeldInstance instance; - - void create(Order order) { - for (Handler handler : instance.handlers()) { - handler.get().process(order); - if (Dependent.class.equals(handler.getBean().getScope()) { - // Destroy only dependent processors - handler.destroy(); - } - } - } - } ----------------------------------------------------------------------------------- +Weld provides `org.jboss.weld.inject.WeldInstance`, which extends `Instance` with selection by `java.lang.reflect.Type` and `getHandlePriorityComparator()`. +The comparator sorts handles by descending bean priority and can be passed to `handlesStream().sorted(instance.getHandlePriorityComparator())`. -Third method is a twist on the `select()` method, but it accepts `java.lang.reflect.Type` as parameter and optionally qualifier(s). +Weld's additional `select()` overload accepts `java.lang.reflect.Type` as a parameter and optionally qualifier(s). This allows for generic selection of instances which can be handy while dealing with third party beans through extensions. However, in order to stay type-safe, this method has a limitation - it can only be invoked on `WeldInstance`. Invocation on any other type than `Object` will result in an `IllegalStateException`. diff --git a/docs/reference/src/main/asciidoc/interceptors.asciidoc b/docs/reference/src/main/asciidoc/interceptors.asciidoc index 94c45a107a6..2b5c50d8cd8 100644 --- a/docs/reference/src/main/asciidoc/interceptors.asciidoc +++ b/docs/reference/src/main/asciidoc/interceptors.asciidoc @@ -336,89 +336,21 @@ in which interceptors are listed at the class level. Therefore, we recommend the use of CDI-style interceptor bindings. -=== Enhanced version of `jakarta.interceptor.InvocationContext` +=== Accessing interceptor bindings -For even more control over interceptors, Weld offers enhanced version of `jakarta.interceptor.InvocationContext` - `org.jboss.weld.interceptor.WeldInvocationContext`. - -NOTE: *The functionality described below is deprecated since Weld 6 and will be removed in the future. Users should instead use newly added methods directly from `jakarta.interceptor.InvocationContext`, hence removing the need to use Weld specific APIs for this purpose.* - -It comes with two additional methods - `getInterceptorBindings` and `getInterceptorBindingsByType(Class annotationType)`. -You shouldn't need this in most cases, but it comes handy when working with `@Nonbinding` values in interceptor binding annotations. - -Assume you have the following interceptor binding: - -[source.JAVA, java] ------------------------------------------------------------------------- -@InterceptorBinding -@Inherited -@Target({ TYPE, METHOD, CONSTRUCTOR}) -@Retention(RUNTIME) -public @interface FooBinding { - - @Nonbinding - String secret() default ""; - -} ------------------------------------------------------------------------- - -Then, in the interceptor class, you can retrieve the secret `String` in the following way: - -[source.JAVA, java] -------------------------------------------------------------------------------------------------- -@Priority(value = Interceptor.Priority.APPLICATION) -@Interceptor -@FooBinding(secret = "nonbinding") -public class AroundConstructInterceptor { - - @SuppressWarnings("unchecked") - @AroundConstruct - void intercept(InvocationContext ctx) throws Exception { - if(ctx instanceof WeldInvocationContext) { - Set bindings = ((WeldInvocationContext)ctx).getInterceptorBindings(); - for (Annotation annotation : bindings) { - if (annotation.annotationType().equals(FooBinding.class)) { - FooBinding fooBinding = (FooBinding) annotation; - String secret = fooBinding.secret(); - } - } - } - ctx.proceed(); - } -} -------------------------------------------------------------------------------------------------- - -`WeldInvocationContext` can be used with the following interceptor types: - -* `@AroundConstruct` -* `@PostConstruct` -* `@AroundInvoke` - -Alternatively, you can gain access to these binding directly from `InvocationContext` as we store them there using a key `org.jboss.weld.interceptor.bindings`. -This key is easily accessible from `WeldInvocationContext.INTERCEPTOR_BINDINGS_KEY`. -Let's alter the previous example to demonstrate this: +Jakarta Interceptors provides `InvocationContext.getInterceptorBindings()` to obtain all bindings and `getInterceptorBindings(Class)` to obtain bindings of a given type. +This is useful for reading `@Nonbinding` members, whose values do not affect interceptor selection. +For example, an interceptor can read a `FooBinding` annotation's `@Nonbinding String secret()` member as follows: [source.JAVA, java] ------------------------------------------------------------------------------------------------- -@Priority(value = Interceptor.Priority.APPLICATION) -@Interceptor -@FooBinding(secret = "nonbinding") -public class AroundConstructInterceptor { - - @SuppressWarnings("unchecked") - @AroundConstruct - void intercept(InvocationContext ctx) throws Exception { - // retrieve data directly from InvocationContext - Set bindings = (Set) ctx.getContextData().get(WeldInvocationContext.INTERCEPTOR_BINDINGS_KEY); - if (bindings != null) { - for (Annotation annotation : bindings) { - if (annotation.annotationType().equals(FooBinding.class)) { - FooBinding fooBinding = (FooBinding) annotation; - String secret = fooBinding.secret(); - } - } - } - ctx.proceed(); +@AroundInvoke +Object intercept(InvocationContext ctx) throws Exception { + for (FooBinding binding : ctx.getInterceptorBindings(FooBinding.class)) { + String secret = binding.secret(); + // Use the value configured on the intercepted bean or method. } + return ctx.proceed(); } ------------------------------------------------------------------------------------------------- diff --git a/docs/reference/src/main/asciidoc/ri-spi.asciidoc b/docs/reference/src/main/asciidoc/ri-spi.asciidoc index 3972d45bcfd..4dc21960c37 100644 --- a/docs/reference/src/main/asciidoc/ri-spi.asciidoc +++ b/docs/reference/src/main/asciidoc/ri-spi.asciidoc @@ -290,13 +290,6 @@ registers an `@EJB` injection point (on a managed bean) and returns a `ResourceReferenceFactory` as explained above. This service is not required if the implementation of <> takes care of `@EJB` injection. -NOTE: `EJBInjectionServices.resolveEjb(InjectionPoint ij)`, which allows -`@EJB` injection point to be resolved without prior registration was -deprecated in Weld 2 and should no longer be used. An injection point -should be registered properly using -`EjbInjectionServices.registerEjbInjectionPoint(InjectionPoint injectionPoint)` -instead. - [[jpa_services]] ==== JPA services @@ -308,25 +301,10 @@ To allow JPA integration, the `org.jboss.weld.injection.spi.JpaInjectionServices should be implemented. This service is not required if the implementation of <> takes care of `@PersistenceContext` injection. -[NOTE] -==== - -The following methods were deprecated in Weld 2: - -* -`JpaInjectionServices.resolvePersistenceContext(InjectionPoint injectionPoint)` - -* -`JpaInjectionServices.resolvePersistenceUnit(InjectionPoint injectionPoint)` - -An injection point should instead be registered properly using the -following methods: - -* -`JpaInjectionServices.registerPersistenceContextInjectionPoint(InjectionPoint injectionPoint)` -* -`JpaInjectionServices.registerPersistenceUnitInjectionPoint(InjectionPoint injectionPoint)` -==== +Register injection points using +`JpaInjectionServices.registerPersistenceContextInjectionPoint(InjectionPoint injectionPoint)` or +`JpaInjectionServices.registerPersistenceUnitInjectionPoint(InjectionPoint injectionPoint)`. +These methods validate the injection point and return a `ResourceReferenceFactory` for obtaining resource references at runtime. ==== Transaction Services @@ -353,26 +331,10 @@ delegated to the container. You must provide an implementation of service is not required if the implementation of <> takes care of `@Resource` injection. -[NOTE] -==== - -The following methods were deprecated in Weld 2: - -* -`ResourceInjectionServices.resolveResource(InjectionPoint injectionPoint)` - -* -`ResourceInjectionServices.resolveResource(String jndiName, String mappedName)` - -An injection point should instead be registered properly using the -following methods: - -* -`ResourceInjectionServices.registerResourceInjectionPoint(InjectionPoint injectionPoint)` - -* -`ResourceInjectionServices.registerResourceInjectionPoint(String jndiName, String mappedName)` -==== +Register resource injection points using +`ResourceInjectionServices.registerResourceInjectionPoint(InjectionPoint injectionPoint)` or +`ResourceInjectionServices.registerResourceInjectionPoint(String jndiName, String mappedName)`. +Both methods return a `ResourceReferenceFactory` for obtaining resource references at runtime. ==== Web Service Injection Services @@ -579,8 +541,8 @@ user configuration, classloader isolation for each CDI application. ==== Servlet If you are integrating Weld into a Servlet environment you must register -`org.jboss.weld.servlet.WeldInitialListener` and -`org.jboss.weld.servlet.WeldTerminalListener` as Servlet listeners, +`org.jboss.weld.module.web.servlet.WeldInitialListener` and +`org.jboss.weld.module.web.servlet.WeldTerminalListener` as Servlet listeners, either automatically, or through user configuration, for each CDI application which uses Servlet. @@ -600,11 +562,11 @@ web descriptor. That allows application to place another filter around the CDI filter for dealing with exceptions. Weld provides this filter with a fully qualified class name -of`org.jboss.weld.servlet.ConversationFilter`. +of `org.jboss.weld.module.web.servlet.ConversationFilter`. If the application contains a filter mapping for a filter named ``CDI Conversation Filter'', the integrator is required to register -`org.jboss.weld.servlet.ConversationFilter` as a filter with ``CDI +`org.jboss.weld.module.web.servlet.ConversationFilter` as a filter with ``CDI Conversation Filter'' as its filter name. If no such mapping exists in the application, the integrator is not required to register the filter. In that case, `WeldInitialListener` will take care of conversation context @@ -612,22 +574,27 @@ activation/deactivation at the beginning of HTTP request processing. ==== JSF +CDI 5 exposes EL integration through `jakarta.enterprise.inject.spi.el.ELAwareBeanManager`, +provided by `jakarta.cdi:jakarta.cdi-el-api`. +The `WeldManager` returned by `Bootstrap.getManager()` implements this interface. +The EL integration methods are no longer declared by `BeanManager`. + If you are integrating Weld into a JSF environment you must register -`org.jboss.weld.el.WeldELContextListener` as an EL Context listener. +`org.jboss.weld.module.web.el.WeldELContextListener` as an EL Context listener. If you are integrating Weld into a JSF environment you must register -`org.jboss.weld.jsf.ConversationAwareViewHandler` as a delegating view +`org.jboss.weld.module.jsf.ConversationAwareViewHandler` as a delegating view handler. If you are integrating Weld into a JSF environment you must obtain the bean manager for the module and then call -`BeanManager.wrapExpressionFactory()`, passing +`ELAwareBeanManager.wrapExpressionFactory()`, passing `Application.getExpressionFactory()` as the argument. The wrapped expression factory must be used in all EL expression evaluations performed by JSF in this web application. If you are integrating Weld into a JSF environment you must obtain the -bean manager for the module and then call `BeanManager.getELResolver()`, +bean manager for the module and then call `ELAwareBeanManager.getELResolver()`, The returned EL resolver should be registered with JSF for this web application. @@ -636,32 +603,22 @@ module. You could call `Bootstrap.getManager()`, passing in the BDA for this module. Alternatively, you could use the injection into Java EE component classes, or look up the bean manager in JNDI. -If you are integrating Weld into a JSF environment you must register -`org.jboss.weld.servlet.ConversationPropagationFilter` as a Servlet -listener, either automatically, or through user configuration, for each -CDI application which uses JSF. This filter can be registered for all -Servlet deployment safely. - - -NOTE: Weld 3 only supports JSF 2.2 and above. - -NOTE: `org.jboss.weld.servlet.ConversationPropagationFilter` was deprecated -and should no longer be used. - ==== JSP +Use the module's `ELAwareBeanManager` for EL integration, as described above. + If you are integrating Weld into a JSP environment you must register -`org.jboss.weld.el.WeldELContextListener` as an EL Context listener. +`org.jboss.weld.module.web.el.WeldELContextListener` as an EL Context listener. If you are integrating Weld into a JSP environment you must obtain the bean manager for the module and then call -`BeanManager.wrapExpressionFactory()`, passing -`Application.getExpressionFactory()` as the argument. The wrapped +`ELAwareBeanManager.wrapExpressionFactory()`, passing +`JspApplicationContext.getExpressionFactory()` as the argument. The wrapped expression factory must be used in all EL expression evaluations performed by JSP. If you are integrating Weld into a JSP environment you must obtain the -bean manager for the module and then call `BeanManager.getELResolver()`, +bean manager for the module and then call `ELAwareBeanManager.getELResolver()`, The returned EL resolver should be registered with JSP for this web application. @@ -882,6 +839,114 @@ This part of the appendix documents the changes in Weld from previous major vers These changes mostly touch changes in the SPI or in the container contract. For information on migration to older Weld versions, please consult https://docs.jboss.org/weld/reference/[their respective documentation]. +[[weld_6_to_7_migration]] +==== Migration from Weld 6.0 to 7.0 + +===== CDI 5 and Java 17 + +Weld 7 implements Jakarta CDI 5.0. Both require Java 17 or newer. +Applications and integrators should review the following changes in addition to the Weld API removals below. + +===== CDI artifact coordinates + +The CDI API coordinates are now `jakarta.cdi:jakarta.cdi-api`, replacing `jakarta.enterprise:jakarta.enterprise.cdi-api`. +Use `jakarta.cdi:jakarta.cdi-el-api` when integrating with Jakarta Expression Language. +CDI 5 provides API relocation artifacts to ease migration, but applications should update their dependency declarations. +The CDI TCK artifacts also move to the `jakarta.cdi` group, with artifact names such as `jakarta.cdi-tck-api`; +the TCK does not provide relocation artifacts. + +===== Expression Language integration + +`BeanManager.getELResolver()` and `BeanManager.wrapExpressionFactory()` have been removed. +Use `jakarta.enterprise.inject.spi.el.ELAwareBeanManager` from the separate CDI EL API instead. +Weld's `WeldManager` extends this interface, so integrators obtaining it through `Bootstrap.getManager()` can call these methods directly. + +===== Bean archives + +The CDI 5 descriptor uses version `5.0` and the `https://jakarta.ee/xml/ns/jakartaee` namespace with `beans_5_0.xsd`. +For example: + +[source,xml] +---- + +---- + +Trimming is no longer permitted in non-explicit bean archives. +Remove `` from descriptors using `bean-discovery-mode="annotated"` or `"none"`. +Use an explicit bean archive (`bean-discovery-mode="all"`) when trimming is intended. + +===== New programming-model features + +* `@Reserve` declares a fallback bean. Select it with `@Priority`; a matching non-reserve bean takes precedence during typesafe resolution. + Reserves cannot be selected through `beans.xml`. +* `@Eager` requests initialization of an `@ApplicationScoped` bean no later than the `Startup` event. +* `@AutoClose` causes `close()` to be called during destruction when the contextual instance implements `AutoCloseable`. + It does not replace the ordinary bean lifecycle or require the bean to implement that interface. +* Application and dependent scopes are global: their contexts have a container-wide lifetime and are active on all application threads during that lifetime. + This does not make instances of dependent beans shared. + +===== Asynchronous invokers + +CDI 5 introduces `AsyncHandler.ReturnType` and `AsyncHandler.ParameterType` service providers. +A method is asynchronous for invocation purposes only when exactly one handler matches it. +A parameter-type handler matches only if exactly one parameter has its async type. +Dependent objects obtained through invoker lookup are retained until both the target method has returned and the handler has signalled completion; +when the target method throws synchronously, they are destroyed before the exception is rethrown. + +Handlers for `CompletionStage`, `CompletableFuture`, and `Flow.Publisher` are built in. +Multiple handlers for the same async type cause a deployment problem unless one is selected explicitly. +Weld supports selection through `org.jboss.weld.invokable.asyncHandlers`; see +ifndef::generate-index-link[<>] +ifdef::generate-index-link[link:configure.html#async_handler_selection[Selecting asynchronous invocation handlers]] +. + +===== Build compatible extensions + +Synthetic beans can declare injection points using `SyntheticBeanBuilder.withInjectionPoint()` and obtain their references through `SyntheticInjections`. +Migrate synthetic creators and disposers to the overloads using `SyntheticInjections` and register the injection points they use. +The older overloads taking `Instance` remain available but are deprecated for removal. +Implement exactly one overload for each creator or disposer. + +`SeContainerInitializer.addBuildCompatibleExtensions()` can register build compatible extensions programmatically in Java SE. +The `@Registration` annotation can now specify parameterized types. + +===== Removal of deprecated Weld APIs + +The deprecated `WeldContainer.instance()` method has been removed. +Use lookup methods directly on the container: + +[source,diff] +---- +-MyBean bean = container.instance().select(MyBean.class).get(); ++MyBean bean = container.select(MyBean.class).get(); +---- + +The deprecated `LiteExtensionTranslator(List, ClassLoader)` constructor has also been removed. +Use `LiteExtensionTranslator(Collection, ClassLoader)` instead. + +Weld 7 removes the deprecated `WeldInstance.Handler` API and its associated methods. +Use the following replacements: + +[options="header"] +|=== +|Removed API |Replacement +|`WeldInstance.Handler` |`Instance.Handle` +|`getHandler()` |`getHandle()` +|`handlers()` |`handles()` +|`handlersStream()` |`handlesStream()` +|`getPriorityComparator()` |`WeldInstance.getHandlePriorityComparator()` +|=== + +The handle APIs are part of CDI; the priority comparator remains a Weld extension. + +Weld 7 also removes `org.jboss.weld.interceptor.WeldInvocationContext` and stops populating the `org.jboss.weld.interceptor.bindings` context-data entry. +The binding key and `getInterceptorBindingsByType(Class)` were deprecated in Weld 6. +Use the standard Jakarta Interceptors `InvocationContext.getInterceptorBindings()` and `getInterceptorBindings(Class)` methods instead. +Interceptors no longer need to cast the invocation context to a Weld-specific interface. + [[weld_4_to_5_migration]] ==== Migration from Weld 4.0 to 5.0 @@ -977,4 +1042,3 @@ If you are looking for deprecation removals in CDI API, take a look at https://g * `WeldInstance.Handler` class is now deprecated and so are multiple methods returning this type from `WeldInstance` ** The reason for this removal is that the very same feature was accepted into CDI specification so you can now use CDI API interfaces to achieve the same. ** For more information, take a look at https://github.com/jakartaee/cdi/blob/master/api/src/main/java/jakarta/enterprise/inject/Instance.java#L266-L315[`Instance.Handle` class from CDI API]. - diff --git a/docs/reference/src/main/asciidoc/scopescontexts.asciidoc b/docs/reference/src/main/asciidoc/scopescontexts.asciidoc index d376f181a89..0ae1ab954ae 100644 --- a/docs/reference/src/main/asciidoc/scopescontexts.asciidoc +++ b/docs/reference/src/main/asciidoc/scopescontexts.asciidoc @@ -38,9 +38,9 @@ scopes by creating a new scope type annotation: [source.JAVA, java] ---------------------------------- -@ScopeType +@NormalScope @Retention(RUNTIME) -@Target({TYPE, METHOD}) +@Target({TYPE, METHOD, FIELD}) public @interface ClusterScoped {} ---------------------------------- diff --git a/environments/se/core/pom.xml b/environments/se/core/pom.xml index 344f0829736..d7b201527ba 100644 --- a/environments/se/core/pom.xml +++ b/environments/se/core/pom.xml @@ -103,14 +103,10 @@ true pom - + org.apache.groovy - groovy-test-junit5 - - - org.apache.groovy - groovy-testng + groovy-test-junit6 diff --git a/environments/se/core/src/main/java/org/jboss/weld/environment/se/WeldContainer.java b/environments/se/core/src/main/java/org/jboss/weld/environment/se/WeldContainer.java index dec5b1f358f..43a84de70d2 100644 --- a/environments/se/core/src/main/java/org/jboss/weld/environment/se/WeldContainer.java +++ b/environments/se/core/src/main/java/org/jboss/weld/environment/se/WeldContainer.java @@ -16,8 +16,6 @@ */ package org.jboss.weld.environment.se; -import java.lang.annotation.Annotation; -import java.lang.reflect.Type; import java.util.Collection; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -29,7 +27,6 @@ import jakarta.enterprise.event.Shutdown; import jakarta.enterprise.event.Startup; import jakarta.enterprise.inject.Any; -import jakarta.enterprise.inject.Instance; import jakarta.enterprise.inject.Vetoed; import jakarta.enterprise.inject.se.SeContainer; import jakarta.enterprise.inject.spi.BeanManager; @@ -250,22 +247,6 @@ private void fireContainerInitializedEvent() { } } - /** - * Deprecated - {@code WeldContainer} implements {@linkjakarta.enterprise.inject.Instance} and applications can - * therefore directly perform programmatic lookup from this object. - * See {@link #select(Type, Annotation...)} and other methods. - *

- * Provides access to all beans within the application. - * - * @return the instance - * @deprecated Applications are encouraged to use methods for programmatic lookup directly. - */ - @Deprecated(forRemoval = true) - public Instance instance() { - checkState(); - return getInstance(); - } - /** * Provides access to all events within the application. For example: * @@ -432,4 +413,4 @@ private BeanDeploymentArchive getDeterminingBeanDeploymentArchive(Deployment dep return deployment.loadBeanDeploymentArchive(WeldContainer.class); } -} \ No newline at end of file +} diff --git a/environments/se/core/src/test/java/org/jboss/weld/environment/se/test/container/isolation/ContainerIsolationTest.java b/environments/se/core/src/test/java/org/jboss/weld/environment/se/test/container/isolation/ContainerIsolationTest.java index 0d17248bc78..5aa36b4ad10 100644 --- a/environments/se/core/src/test/java/org/jboss/weld/environment/se/test/container/isolation/ContainerIsolationTest.java +++ b/environments/se/core/src/test/java/org/jboss/weld/environment/se/test/container/isolation/ContainerIsolationTest.java @@ -36,7 +36,7 @@ public void testContainerIsolation() { Weld weld1 = new Weld("1"); WeldContainer weldContainer1 = weld1.initialize(); - Foo foo1 = weldContainer1.instance().select(Foo.class).get(); + Foo foo1 = weldContainer1.select(Foo.class).get(); Weld weld2 = new Weld("2"); WeldContainer weldContainer2 = weld2.initialize(); diff --git a/environments/se/tests/src/test/java/org/jboss/weld/environment/se/test/instance/enhanced/InstanceHandleTest.java b/environments/se/tests/src/test/java/org/jboss/weld/environment/se/test/instance/enhanced/InstanceHandleTest.java index 52c59dfd9b4..8cf396d339b 100644 --- a/environments/se/tests/src/test/java/org/jboss/weld/environment/se/test/instance/enhanced/InstanceHandleTest.java +++ b/environments/se/tests/src/test/java/org/jboss/weld/environment/se/test/instance/enhanced/InstanceHandleTest.java @@ -46,7 +46,7 @@ import org.junit.runner.RunWith; /** - * Similar to {@link WeldInstanceTest} but uses purely CDI APIs + * Tests instance handles and Weld's handle priority comparator. * * @author Matej Novotny */ diff --git a/environments/se/tests/src/test/java/org/jboss/weld/environment/se/test/instance/enhanced/WeldInstanceTest.java b/environments/se/tests/src/test/java/org/jboss/weld/environment/se/test/instance/enhanced/WeldInstanceTest.java deleted file mode 100644 index 5528be46df7..00000000000 --- a/environments/se/tests/src/test/java/org/jboss/weld/environment/se/test/instance/enhanced/WeldInstanceTest.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * JBoss, Home of Professional Open Source - * Copyright 2016, Red Hat, Inc., and individual contributors - * by the @authors tag. See the copyright.txt in the distribution for a - * full listing of individual contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jboss.weld.environment.se.test.instance.enhanced; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.math.BigDecimal; -import java.util.Iterator; -import java.util.List; - -import jakarta.enterprise.context.Dependent; -import jakarta.enterprise.inject.spi.Bean; - -import org.jboss.arquillian.container.se.api.ClassPath; -import org.jboss.arquillian.container.test.api.Deployment; -import org.jboss.arquillian.junit.Arquillian; -import org.jboss.shrinkwrap.api.Archive; -import org.jboss.shrinkwrap.api.BeanArchive; -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.weld.environment.se.Weld; -import org.jboss.weld.environment.se.WeldContainer; -import org.jboss.weld.inject.WeldInstance; -import org.jboss.weld.inject.WeldInstance.Handler; -import org.jboss.weld.test.util.ActionSequence; -import org.jboss.weld.test.util.Utils; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * @author Matej Novotny - * @author Martin Kouba - */ -@RunWith(Arquillian.class) -public class WeldInstanceTest { - - @Deployment - public static Archive createTestArchive() { - return ClassPath.builder() - .add(ShrinkWrap.create(BeanArchive.class, Utils.getDeploymentNameAsHash(WeldInstanceTest.class)) - .addPackage(WeldInstanceTest.class.getPackage()).addClass(ActionSequence.class)) - .build(); - } - - @Test - public void testIsResolvable() { - try (WeldContainer container = new Weld().initialize()) { - assertTrue(container.select(Alpha.class).isResolvable()); - assertFalse(container.select(BigDecimal.class, Juicy.Literal.INSTANCE).isResolvable()); - } - } - - @Test - public void testGetHandler() { - ActionSequence.reset(); - try (WeldContainer container = new Weld().initialize()) { - - Bean alphaBean = container.getBeanManager().resolve(container.getBeanManager().getBeans(Alpha.class)); - WeldInstance instance = container.select(Alpha.class); - - Handler alpha1 = instance.getHandler(); - assertEquals(alphaBean, alpha1.getBean()); - assertEquals(Dependent.class, alpha1.getBean().getScope()); - // Contextual reference is obtained lazily - assertNull(ActionSequence.getSequenceData()); - - String alpha2Id; - - // Test try-with-resource - try (Handler alpha2 = instance.getHandler()) { - assertNull(ActionSequence.getSequenceData()); - alpha2Id = alpha2.get().getId(); - assertFalse(alpha1.get().getId().equals(alpha2Id)); - } - - List sequence = ActionSequence.getSequenceData(); - assertEquals(3, sequence.size()); - assertEquals("c" + alpha2Id, sequence.get(0)); - assertEquals("c" + alpha1.get().getId(), sequence.get(1)); - assertEquals("d" + alpha2Id, sequence.get(2)); - - alpha1.destroy(); - // Alpha1 destroyed - sequence = ActionSequence.getSequenceData(); - assertEquals(4, sequence.size()); - // Subsequent invocations are no-op - alpha1.destroy(); - - // Test normal scoped bean is also destroyed - WeldInstance bravoInstance = container.select(Bravo.class); - String bravoId = bravoInstance.get().getId(); - try (Handler bravo = bravoInstance.getHandler()) { - assertEquals(bravoId, bravo.get().getId()); - ActionSequence.reset(); - } - sequence = ActionSequence.getSequenceData(); - assertEquals(1, sequence.size()); - assertEquals("d" + bravoId, sequence.get(0)); - } - } - - @Test - public void testGetAfterDestroyingContextualInstance() { - ActionSequence.reset(); - try (WeldContainer container = new Weld().initialize()) { - WeldClient client = container.select(WeldClient.class).get(); - assertNotNull(client); - - Handler alphaHandle = client.getAlphaInstance().getHandler(); - // trigger bean creation and assert - alphaHandle.get(); - List sequence = ActionSequence.getSequenceData(); - assertEquals(1, sequence.size()); - // trigger bean destruction - alphaHandle.destroy(); - // verify that the destruction happened - sequence = ActionSequence.getSequenceData(); - assertEquals(2, sequence.size()); - - // try to invoke Handle.get() again; this should throw an exception - try { - alphaHandle.get(); - Assert.fail("Invoking Handle.get() after destroying contextual instance should throw an exception."); - } catch (IllegalStateException e) { - // expected - } - } - } - - @Test - public void testHandlers() { - ActionSequence.reset(); - try (WeldContainer container = new Weld().initialize()) { - WeldInstance instance = container.select(Processor.class); - assertTrue(instance.isAmbiguous()); - for (Handler handler : instance.handlers()) { - handler.get().ping(); - if (handler.getBean().getScope().equals(Dependent.class)) { - handler.destroy(); - } - } - assertEquals(3, ActionSequence.getSequenceSize()); - ActionSequence.assertSequenceDataContainsAll("firstPing", "secondPing", "firstDestroy"); - - ActionSequence.reset(); - assertTrue(instance.isAmbiguous()); - for (Iterator> iterator = instance.handlers().iterator(); iterator.hasNext();) { - try (Handler handler = iterator.next()) { - handler.get().ping(); - } - } - assertEquals(4, ActionSequence.getSequenceSize()); - ActionSequence.assertSequenceDataContainsAll("firstPing", "secondPing", "firstDestroy", "secondDestroy"); - } - - } - - @Test - public void testHandlersStream() { - ActionSequence.reset(); - try (WeldContainer container = new Weld().initialize()) { - - Handler processor = container.select(Processor.class).handlersStream() - .filter(h -> Dependent.class.equals(h.getBean().getScope())) - .findFirst().orElse(null); - assertNull(ActionSequence.getSequenceData()); - assertNotNull(processor); - assertEquals(FirstProcessor.class, processor.getBean().getBeanClass()); - assertEquals(FirstProcessor.class.getName(), processor.get().getId()); - processor.destroy(); - List sequence = ActionSequence.getSequenceData(); - assertEquals(1, sequence.size()); - assertEquals("firstDestroy", sequence.get(0)); - - Handler withPriority = container.select(WithPriority.class).handlersStream() - .sorted(container.getPriorityComparator()).findFirst() - .orElse(null); - assertNotNull(withPriority); - assertEquals(Priority3.class, withPriority.getBean().getBeanClass()); - withPriority.get(); - sequence = ActionSequence.getSequenceData(); - assertEquals(2, sequence.size()); - assertEquals("firstDestroy", sequence.get(0)); - assertEquals("c" + Priority3.class.getName(), sequence.get(1)); - } - } -} diff --git a/examples/se/numberguess/README.md b/examples/se/numberguess/README.md index 4a27c1dc5ff..444e2ef2379 100644 --- a/examples/se/numberguess/README.md +++ b/examples/se/numberguess/README.md @@ -75,10 +75,8 @@ The game's main logic is located in `Game.java`. In this example, it differs fro * Since there is no dedicated validation phase, validation of user input is performed during the `check()` method. -* The `reset()` method makes a call to the injected `rndGenerator` in order to get - the random number at the start of each game. Note that it cannot use - `manager.getInstanceByType(Integer.class, new AnnotationLiteral(){})` - as the JSF example does because there will not be any active contexts like there - is during a JSF request. +* The `reset()` method calls the injected `rndGenerator` to obtain a new random + number at the start of each game. The JSF example instead uses an injected + `@Random Instance` and calls `get()` for each new game. For a deeper look into the SE Number Guess example, please refer to chapter 7.2 of the reference documentation. diff --git a/impl/src/main/java/org/jboss/weld/AbstractCDI.java b/impl/src/main/java/org/jboss/weld/AbstractCDI.java index 7a68c67d82a..de9e0990cd2 100644 --- a/impl/src/main/java/org/jboss/weld/AbstractCDI.java +++ b/impl/src/main/java/org/jboss/weld/AbstractCDI.java @@ -38,7 +38,6 @@ import org.jboss.weld.util.cache.ComputingCache; import org.jboss.weld.util.cache.ComputingCacheBuilder; import org.jboss.weld.util.collections.ImmutableSet; -import org.jboss.weld.util.reflection.Reflections; /** * Abstract implementation of CDI which forwards all Instance methods to a delegate. Furthermore, it allows the calling class to @@ -114,11 +113,6 @@ public void destroy(T instance) { instanceInternal().destroy(instance); } - @Override - public Handler getHandler() { - return getInstance().getHandler(); - } - @Override public Handle getHandle() { return getInstance().getHandle(); @@ -129,22 +123,11 @@ public boolean isResolvable() { return getInstance().isResolvable(); } - @Override - public Iterable> handlers() { - return getInstance().handlers(); - } - @Override public Iterable> handles() { return getInstance().handles(); } - @Override - public Comparator> getPriorityComparator() { - return Reflections.cast(new PriorityComparator( - BeanManagerProxy.unwrap(getBeanManager()).getServices().get(AnnotationApiAbstraction.class))); - } - @Override public Comparator> getHandlePriorityComparator() { return new PriorityComparator( diff --git a/impl/src/main/java/org/jboss/weld/bean/InterceptorImpl.java b/impl/src/main/java/org/jboss/weld/bean/InterceptorImpl.java index 8520668069d..85aa2b0947b 100644 --- a/impl/src/main/java/org/jboss/weld/bean/InterceptorImpl.java +++ b/impl/src/main/java/org/jboss/weld/bean/InterceptorImpl.java @@ -39,7 +39,6 @@ import org.jboss.weld.manager.BeanManagerImpl; import org.jboss.weld.util.Interceptors; import org.jboss.weld.util.reflection.Formats; -import org.jboss.weld.util.reflection.Reflections; /** * @author Marius Bogoevici @@ -91,11 +90,7 @@ public Object intercept(InterceptionType type, T instance, final InvocationConte .getInterceptorInvocation(instance, interceptionType) .getInterceptorMethodInvocations(); - Set interceptorBindings = null; - if (ctx instanceof org.jboss.weld.interceptor.WeldInvocationContext) { - interceptorBindings = Reflections. cast(ctx) - .getInterceptorBindings(); - } + Set interceptorBindings = ctx.getInterceptorBindings(); try { /* diff --git a/impl/src/main/java/org/jboss/weld/bean/builtin/InstanceImpl.java b/impl/src/main/java/org/jboss/weld/bean/builtin/InstanceImpl.java index cc00200118a..66e1ce1f1ab 100644 --- a/impl/src/main/java/org/jboss/weld/bean/builtin/InstanceImpl.java +++ b/impl/src/main/java/org/jboss/weld/bean/builtin/InstanceImpl.java @@ -215,12 +215,7 @@ public void destroy(T instance) { @Override public Handle getHandle() { checkBeanResolved(); - return new HandlerImpl(() -> getBeanInstance(bean), this, bean); - } - - @Override - public Handler getHandler() { - return Reflections.cast(getHandle()); + return new HandleImpl(() -> getBeanInstance(bean), this, bean); } @Override @@ -233,16 +228,6 @@ public Iterable> handles() { return () -> new HandleIterator(allBeans()); } - @Override - public Iterable> handlers() { - return Reflections.cast(handles()); - } - - @Override - public Comparator> getPriorityComparator() { - return Reflections.cast(getHandlePriorityComparator()); - } - @Override public Comparator> getHandlePriorityComparator() { return new PriorityComparator(getBeanManager().getServices().get(AnnotationApiAbstraction.class)); @@ -363,12 +348,12 @@ private HandleIterator(Set> beans) { @Override public Handle next() { Bean bean = delegate.next(); - return new HandlerImpl<>(() -> getBeanInstance(bean), InstanceImpl.this, bean); + return new HandleImpl<>(() -> getBeanInstance(bean), InstanceImpl.this, bean); } } - private static class HandlerImpl implements Handler { + private static class HandleImpl implements Handle { private final LazyValueHolder value; @@ -378,7 +363,7 @@ private static class HandlerImpl implements Handler { private final AtomicBoolean isDestroyed; - HandlerImpl(Supplier supplier, WeldInstance instance, Bean bean) { + HandleImpl(Supplier supplier, WeldInstance instance, Bean bean) { this.value = LazyValueHolder.forSupplier(supplier); this.bean = bean; this.instance = new WeakReference<>(instance); @@ -422,7 +407,7 @@ public void close() { @Override public String toString() { - return "HandlerImpl [bean=" + bean + "]"; + return "HandleImpl [bean=" + bean + "]"; } } diff --git a/impl/src/main/java/org/jboss/weld/bean/builtin/PriorityComparator.java b/impl/src/main/java/org/jboss/weld/bean/builtin/PriorityComparator.java index 7470b37c73a..f71b49181c9 100644 --- a/impl/src/main/java/org/jboss/weld/bean/builtin/PriorityComparator.java +++ b/impl/src/main/java/org/jboss/weld/bean/builtin/PriorityComparator.java @@ -29,7 +29,6 @@ /** * * @author Martin Kouba - * @see WeldInstance#getPriorityComparator() * @see WeldInstance#getHandlePriorityComparator() */ public class PriorityComparator implements Comparator> { @@ -58,4 +57,4 @@ private int getPriority(Instance.Handle handler) { } return 0; } -} \ No newline at end of file +} diff --git a/impl/src/main/java/org/jboss/weld/bootstrap/WeldStartup.java b/impl/src/main/java/org/jboss/weld/bootstrap/WeldStartup.java index 0890ddf75e9..a55ee899b3e 100644 --- a/impl/src/main/java/org/jboss/weld/bootstrap/WeldStartup.java +++ b/impl/src/main/java/org/jboss/weld/bootstrap/WeldStartup.java @@ -320,7 +320,8 @@ private void addImplementationServices(ServiceRegistry services) { services.add(CurrentEventMetadata.class, new CurrentEventMetadata()); services.add(SpecializationAndEnablementRegistry.class, new SpecializationAndEnablementRegistry()); services.add(MissingDependenciesRegistry.class, new MissingDependenciesRegistry()); - services.add(AsyncHandlerRegistry.class, new AsyncHandlerRegistry()); + services.add(AsyncHandlerRegistry.class, + new AsyncHandlerRegistry(configuration.getStringProperty(ConfigurationKey.ASYNC_HANDLERS))); /* * Setup ExecutorServices @@ -460,6 +461,7 @@ public void deployBeans() { asyncHandlerRegistry.discoverHandlers(resourceLoader); } } + asyncHandlerRegistry.validateHandlers(); for (BeanDeployment deployment : getBeanDeployments()) { deployment.createBeans(environment); } diff --git a/impl/src/main/java/org/jboss/weld/config/ConfigurationKey.java b/impl/src/main/java/org/jboss/weld/config/ConfigurationKey.java index d6907295719..6019014351a 100644 --- a/impl/src/main/java/org/jboss/weld/config/ConfigurationKey.java +++ b/impl/src/main/java/org/jboss/weld/config/ConfigurationKey.java @@ -277,7 +277,7 @@ public enum ConfigurationKey { * * @see #UNUSED_BEANS_EXCLUDE_TYPE */ - UNUSED_BEANS_EXCLUDE_ANNOTATION("org.jboss.weld.bootstrap.unusedBeans.excludeAnnotation", "javax\\.ws\\.rs.*"), + UNUSED_BEANS_EXCLUDE_ANNOTATION("org.jboss.weld.bootstrap.unusedBeans.excludeAnnotation", "jakarta\\.ws\\.rs.*"), /** * If set to true then when a contextual reference for a @SessionScoped or @ConversationScoped bean is obtained from a @@ -288,6 +288,12 @@ public enum ConfigurationKey { @Description("If set to true then when a contextual reference for a @SessionScoped or @ConversationScoped bean is obtained from a context backed by an HTTP session the instance is set again using HttpSession.setAttribute(). This allows to trigger session replication in some application servers.") RESET_HTTP_SESSION_ATTR_ON_BEAN_ACCESS("org.jboss.weld.context.resetHttpSessionAttributeOnBeanAccess", false), + /** + * Comma-separated async-type=handler-class mappings, using binary class names. + * Selected handlers must be registered providers (or built-in handlers). + */ + ASYNC_HANDLERS("org.jboss.weld.invokable.asyncHandlers", ""), + ; /** diff --git a/impl/src/main/java/org/jboss/weld/config/WeldConfiguration.java b/impl/src/main/java/org/jboss/weld/config/WeldConfiguration.java index 59d30a5e402..ad4d395ba0b 100644 --- a/impl/src/main/java/org/jboss/weld/config/WeldConfiguration.java +++ b/impl/src/main/java/org/jboss/weld/config/WeldConfiguration.java @@ -38,12 +38,10 @@ import org.jboss.weld.exceptions.IllegalStateException; import org.jboss.weld.logging.BeanLogger; import org.jboss.weld.logging.ConfigurationLogger; -import org.jboss.weld.resources.WeldClassLoaderResourceLoader; import org.jboss.weld.resources.spi.ResourceLoader; import org.jboss.weld.resources.spi.ResourceLoadingException; import org.jboss.weld.util.Preconditions; import org.jboss.weld.util.collections.ImmutableMap; -import org.jboss.weld.util.reflection.Reflections; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -106,8 +104,6 @@ public class WeldConfiguration implements Service { private static final String OBSOLETE_SYSTEM_PROPETIES = "obsolete system properties"; - private static final String EXTERNAL_CONFIGURATION_CLASS_NAME = "org.jboss.weld.configuration.spi.ExternalConfiguration"; - private final Map properties; private final File proxyDumpFilePath; @@ -422,12 +418,9 @@ private Map processExternalConfiguration(Map getExternalConfigurationOptions(ServiceRegistry services) { - // to stay compatible with older SPI versions we first check if ExternalConfiguration is available before using the class - if (Reflections.isClassLoadable(EXTERNAL_CONFIGURATION_CLASS_NAME, WeldClassLoaderResourceLoader.INSTANCE)) { - final ExternalConfiguration externalConfiguration = services.get(ExternalConfiguration.class); - if (externalConfiguration != null) { - return externalConfiguration.getConfigurationProperties(); - } + final ExternalConfiguration externalConfiguration = services.get(ExternalConfiguration.class); + if (externalConfiguration != null) { + return externalConfiguration.getConfigurationProperties(); } return Collections.emptyMap(); } diff --git a/impl/src/main/java/org/jboss/weld/interceptor/proxy/AbstractInvocationContext.java b/impl/src/main/java/org/jboss/weld/interceptor/proxy/AbstractInvocationContext.java index 1ee531345ad..13a61508978 100644 --- a/impl/src/main/java/org/jboss/weld/interceptor/proxy/AbstractInvocationContext.java +++ b/impl/src/main/java/org/jboss/weld/interceptor/proxy/AbstractInvocationContext.java @@ -24,14 +24,14 @@ import java.util.Map; import java.util.Set; -import org.jboss.weld.interceptor.WeldInvocationContext; -import org.jboss.weld.util.Preconditions; +import jakarta.interceptor.InvocationContext; + import org.jboss.weld.util.Primitives; import org.jboss.weld.util.collections.ImmutableSet; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -abstract class AbstractInvocationContext implements WeldInvocationContext { +abstract class AbstractInvocationContext implements InvocationContext { protected Map contextData; protected final Method method; @@ -76,17 +76,11 @@ protected AbstractInvocationContext(Object target, Method method, Method proceed @Override public Map getContextData() { if (contextData == null) { - contextData = newContextData(interceptorBindings); + contextData = new HashMap<>(); } return contextData; } - protected static Map newContextData(Set interceptorBindings) { - Map result = new HashMap(); - result.put(WeldInvocationContext.INTERCEPTOR_BINDINGS_KEY, interceptorBindings); - return result; - } - @Override public Method getMethod() { return method; @@ -206,16 +200,6 @@ public Constructor getConstructor() { return constructor; } - @Override - @SuppressWarnings("unchecked") - public Set getInterceptorBindingsByType(Class annotationType) { - Preconditions.checkArgumentNotNull(annotationType, "annotationType"); - return interceptorBindings.stream() - .filter((annotation) -> annotation.annotationType().equals(annotationType)) - .map(annotation -> (T) annotation) - .collect(ImmutableSet.collector()); - } - @Override public Set getInterceptorBindings() { return interceptorBindings; diff --git a/impl/src/main/java/org/jboss/weld/interceptor/proxy/InterceptorMethodHandler.java b/impl/src/main/java/org/jboss/weld/interceptor/proxy/InterceptorMethodHandler.java index 93449e7c463..914b994bbb7 100644 --- a/impl/src/main/java/org/jboss/weld/interceptor/proxy/InterceptorMethodHandler.java +++ b/impl/src/main/java/org/jboss/weld/interceptor/proxy/InterceptorMethodHandler.java @@ -12,6 +12,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import jakarta.interceptor.InvocationContext; + import org.jboss.weld.bean.proxy.InterceptionDecorationContext; import org.jboss.weld.bean.proxy.InterceptionDecorationContext.Stack; import org.jboss.weld.bean.proxy.StackAwareMethodHandler; @@ -84,7 +86,7 @@ protected Object executeLifecycleInterception(Object instance, Method method, Me protected Object executeAroundInvoke(Object instance, Method method, Method proceed, Object[] args, CachedInterceptionChain chain, Stack stack) throws Throwable { - org.jboss.weld.interceptor.WeldInvocationContext ctx = create(instance, method, proceed, args, chain.interceptorMethods, + InvocationContext ctx = create(instance, method, proceed, args, chain.interceptorMethods, chain.interceptorBindings, stack); try { return chain.interceptorMethods.get(0).invoke(ctx); diff --git a/impl/src/main/java/org/jboss/weld/interceptor/proxy/NonTerminalAroundInvokeInvocationContext.java b/impl/src/main/java/org/jboss/weld/interceptor/proxy/NonTerminalAroundInvokeInvocationContext.java index 6f5f236dba4..760572ec255 100644 --- a/impl/src/main/java/org/jboss/weld/interceptor/proxy/NonTerminalAroundInvokeInvocationContext.java +++ b/impl/src/main/java/org/jboss/weld/interceptor/proxy/NonTerminalAroundInvokeInvocationContext.java @@ -18,6 +18,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -25,7 +26,6 @@ import jakarta.interceptor.InvocationContext; import org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler; -import org.jboss.weld.interceptor.WeldInvocationContext; /** * The non-terminal {@link InvocationContext} in the interception chain. This implementation is used for the first n-1 @@ -47,7 +47,7 @@ class NonTerminalAroundInvokeInvocationContext extends AroundInvokeInvocationCon public NonTerminalAroundInvokeInvocationContext(Object target, Method method, Method proceed, Object[] parameters, Set interceptorBindings, List chain, CombinedInterceptorAndDecoratorStackMethodHandler currentHandler) { - this(target, method, proceed, parameters, newContextData(interceptorBindings), interceptorBindings, 0, chain, + this(target, method, proceed, parameters, new HashMap<>(), interceptorBindings, 0, chain, currentHandler); } @@ -68,11 +68,11 @@ private NonTerminalAroundInvokeInvocationContext(Object target, Method method, M @Override public Object proceedInternal() throws Exception { - WeldInvocationContext ctx = createNextContext(); + InvocationContext ctx = createNextContext(); return chain.get(position + 1).invoke(ctx); } - private WeldInvocationContext createNextContext() { + private InvocationContext createNextContext() { if (position + 2 == chain.size()) { return new TerminalAroundInvokeInvocationContext(this); } else { diff --git a/impl/src/main/java/org/jboss/weld/interceptor/proxy/WeldInvocationContextImpl.java b/impl/src/main/java/org/jboss/weld/interceptor/proxy/WeldInvocationContextImpl.java index d42ce942d92..d2ded8d84e9 100644 --- a/impl/src/main/java/org/jboss/weld/interceptor/proxy/WeldInvocationContextImpl.java +++ b/impl/src/main/java/org/jboss/weld/interceptor/proxy/WeldInvocationContextImpl.java @@ -21,7 +21,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -31,10 +30,8 @@ import org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler; import org.jboss.weld.bean.proxy.InterceptionDecorationContext; import org.jboss.weld.bean.proxy.InterceptionDecorationContext.Stack; -import org.jboss.weld.interceptor.WeldInvocationContext; import org.jboss.weld.logging.InterceptorLogger; import org.jboss.weld.util.ForwardingInvocationContext; -import org.jboss.weld.util.Preconditions; /** * Weld's {@link InvocationContext} implementation. This is a forwarding implementation that delegates most method calls to an @@ -49,8 +46,7 @@ * @author Jozef Hartinger * */ -public class WeldInvocationContextImpl extends ForwardingInvocationContext - implements org.jboss.weld.interceptor.WeldInvocationContext { +public class WeldInvocationContextImpl extends ForwardingInvocationContext { private int position; private final List chain; @@ -80,7 +76,6 @@ public WeldInvocationContextImpl(InvocationContext delegate, List Set getInterceptorBindingsByType(Class annotationType) { - Preconditions.checkArgumentNotNull(annotationType, "annotationType"); - Set result = new HashSet<>(); - for (Annotation interceptorBinding : interceptorBindings) { - if (interceptorBinding.annotationType().equals(annotationType)) { - result.add((T) interceptorBinding); - } - } - return result; - } - @Override public Set getInterceptorBindings() { return interceptorBindings; diff --git a/impl/src/main/java/org/jboss/weld/invokable/AbstractInvokerBuilder.java b/impl/src/main/java/org/jboss/weld/invokable/AbstractInvokerBuilder.java index f37bd6f006c..1b85d6ea27b 100644 --- a/impl/src/main/java/org/jboss/weld/invokable/AbstractInvokerBuilder.java +++ b/impl/src/main/java/org/jboss/weld/invokable/AbstractInvokerBuilder.java @@ -293,11 +293,10 @@ InvokerInfo doBuild() { // Check for async handler match AsyncHandlerRegistry asyncRegistry = ((BeanManagerImpl) beanManager).getServices().get(AsyncHandlerRegistry.class); - AsyncHandlerRegistry.HandlerInfo returnTypeHandler = asyncRegistry - .findReturnTypeHandler(reflectionMethod.getReturnType()); - AsyncHandlerRegistry.HandlerInfo paramTypeHandler = returnTypeHandler == null - ? asyncRegistry.findParameterTypeHandler(reflectionMethod.getParameterTypes()) - : null; + AsyncHandlerRegistry.HandlerInfo handler = asyncRegistry.findHandler(reflectionMethod.getReturnType(), + reflectionMethod.getParameterTypes()); + AsyncHandlerRegistry.HandlerInfo returnTypeHandler = handler != null && handler.isReturnType() ? handler : null; + AsyncHandlerRegistry.HandlerInfo paramTypeHandler = handler != null && !handler.isReturnType() ? handler : null; // cleanup if (requiresCleanup) { MethodHandle cleanupMethod; @@ -435,22 +434,11 @@ InvokerInfo doBuild() { mh = MethodHandles.catchException(mh, ClassCastException.class, cceCatch); } - // create an inner invoker and pass it to wrapper - // NOTE: invocation wrappers combined with ParameterType async handlers are not - // currently supported — the inner InvokerImpl does not pass CleanupActions - if (invocationWrapper != null) { - InvokerImpl invoker = new InvokerImpl<>(mh); - - MethodHandle invocationWrapperMethod = MethodHandleUtils.createMethodHandleFromTransformer(reflectionMethod, - invocationWrapper, reflectionBeanClass); - - mh = MethodHandles.insertArguments(invocationWrapperMethod, 2, invoker); - } - // ReturnType handler is embedded in the MH chain (tryFinally or filterReturnValue), - // so a plain InvokerImpl suffices. ParameterType needs AsyncInvokerImpl because - // transformArgument must run before the chain but needs the completion callback - // that is only available after. + // so a plain InvokerImpl suffices. ParameterType needs AsyncInvokerImpl to create + // cleanup state before transformArgument and share it with the MH chain, + // allowing the completion callback to coordinate deferred cleanup. + InvokerInfo invoker; if (returnTypeHandler != null) { if (!requiresCleanup) { MethodHandle transform = MethodHandleUtils.APPLY_RETURN_TYPE_HANDLER; @@ -461,12 +449,20 @@ InvokerInfo doBuild() { .changeParameterType(0, mh.type().returnType())); mh = MethodHandles.filterReturnValue(mh, transform); } - return new InvokerImpl<>(mh); + invoker = new InvokerImpl<>(mh); } else if (paramTypeHandler != null) { int asyncParamIndex = findAsyncParamIndex(reflectionMethod.getParameterTypes(), paramTypeHandler.getAsyncType()); - return new AsyncInvokerImpl<>(mh, paramTypeHandler, asyncParamIndex, requiresCleanup); + invoker = new AsyncInvokerImpl<>(mh, paramTypeHandler, asyncParamIndex, requiresCleanup); + } else { + invoker = new InvokerImpl<>(mh); + } + // Each wrapper delegation must perform its own transformations and cleanup. + if (invocationWrapper != null) { + MethodHandle wrapper = MethodHandleUtils.createMethodHandleFromTransformer(reflectionMethod, + invocationWrapper, reflectionBeanClass); + return new InvokerImpl<>(MethodHandles.insertArguments(wrapper, 2, invoker)); } - return new InvokerImpl<>(mh); + return invoker; } private int findAsyncParamIndex(Class[] parameterTypes, Class asyncType) { diff --git a/impl/src/main/java/org/jboss/weld/invokable/AsyncHandlerRegistry.java b/impl/src/main/java/org/jboss/weld/invokable/AsyncHandlerRegistry.java index 402936a8fa5..275ae145923 100644 --- a/impl/src/main/java/org/jboss/weld/invokable/AsyncHandlerRegistry.java +++ b/impl/src/main/java/org/jboss/weld/invokable/AsyncHandlerRegistry.java @@ -4,7 +4,9 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.Flow; @@ -29,10 +31,26 @@ public class AsyncHandlerRegistry implements Service { // Maps async type (erasure) to handler info private final Map, HandlerInfo> handlers = new HashMap<>(); + private final Map, Map, HandlerInfo>> candidates = new HashMap<>(); + private final Map selections = new HashMap<>(); + /** * Creates a new registry with built-in handlers pre-registered. */ public AsyncHandlerRegistry() { + this(""); + } + + public AsyncHandlerRegistry(String configuration) { + if (!configuration.isBlank()) { + for (String entry : configuration.split(",", -1)) { + String[] mapping = entry.split("=", -1); + if (mapping.length != 2 || mapping[0].isBlank() || mapping[1].isBlank() + || selections.putIfAbsent(mapping[0].trim(), mapping[1].trim()) != null) { + throw InvokerLogger.LOG.invalidAsyncHandlerConfiguration(configuration); + } + } + } // Built-in handlers required by the spec registerBuiltinReturnTypeHandler(CompletionStage.class, new BuiltinCompletionStageHandler()); registerBuiltinReturnTypeHandler(CompletableFuture.class, new BuiltinCompletableFutureHandler()); @@ -41,8 +59,8 @@ public AsyncHandlerRegistry() { private void registerBuiltinReturnTypeHandler(Class asyncType, AsyncHandler.ReturnType handler) { HandlerInfo info = HandlerInfo.returnType(handler, asyncType); - info.setBuiltin(true); handlers.put(asyncType, info); + register(info); } /** @@ -68,19 +86,21 @@ private void validateAndRegisterReturnType(AsyncHandler.ReturnType handler) { Class handlerClass = handler.getClass(); validateDirectImplementation(handlerClass, AsyncHandler.ReturnType.class); Class asyncType = extractAsyncType(handlerClass, AsyncHandler.ReturnType.class); - checkDuplicate(asyncType, handlerClass, true); - handlers.putIfAbsent(asyncType, HandlerInfo.returnType(handler, asyncType)); + register(HandlerInfo.returnType(handler, asyncType)); } private void validateAndRegisterParameterType(AsyncHandler.ParameterType handler) { Class handlerClass = handler.getClass(); validateDirectImplementation(handlerClass, AsyncHandler.ParameterType.class); Class asyncType = extractAsyncType(handlerClass, AsyncHandler.ParameterType.class); - checkDuplicate(asyncType, handlerClass, false); - handlers.putIfAbsent(asyncType, HandlerInfo.parameterType(handler, asyncType)); + register(HandlerInfo.parameterType(handler, asyncType)); } private void validateDirectImplementation(Class handlerClass, Class targetInterface) { + if (AsyncHandler.ReturnType.class.isAssignableFrom(handlerClass) + && AsyncHandler.ParameterType.class.isAssignableFrom(handlerClass)) { + throw InvokerLogger.LOG.asyncHandlerBothKinds(handlerClass); + } for (Class iface : handlerClass.getInterfaces()) { if (iface == targetInterface) { return; @@ -89,23 +109,42 @@ private void validateDirectImplementation(Class handlerClass, Class target throw InvokerLogger.LOG.asyncHandlerIndirectImplementation(handlerClass); } - private void checkDuplicate(Class asyncType, Class handlerClass, boolean isReturnType) { - HandlerInfo existing = handlers.get(asyncType); - if (existing != null && !existing.isBuiltin()) { - // In a WAR with multiple BDAs (e.g. WEB-INF/classes + JARs in WEB-INF/lib), - // all BDAs share the same classloader. Since discovery runs per BDA, the same - // service file is found multiple times, yielding the same handler class. - // This is not an error — skip re-registration. In an EAR with isolated module - // classloaders, different Class objects would be loaded, so this identity check - // does not suppress genuine duplicates across modules. - if (existing.getHandlerClass() == handlerClass && existing.isReturnType() == isReturnType) { - return; + private void register(HandlerInfo info) { + // Repeated discovery through BDAs sharing a classloader is not a duplicate provider. + Map, HandlerInfo> providers = candidates.computeIfAbsent(info.getAsyncType(), key -> new HashMap<>()); + providers.putIfAbsent(info.getHandlerClass(), info); + } + + /** + * Resolves configured providers after discovery in every bean deployment archive has completed. + */ + public void validateHandlers() { + Map, HandlerInfo> resolved = new HashMap<>(); + Set unresolved = new HashSet<>(selections.keySet()); + for (Map.Entry, Map, HandlerInfo>> entry : candidates.entrySet()) { + Class asyncType = entry.getKey(); + String selection = selections.get(asyncType.getName()); + HandlerInfo chosen = null; + for (HandlerInfo candidate : entry.getValue().values()) { + if (selection == null || candidate.getHandlerClass().getName().equals(selection)) { + if (chosen != null) { + throw InvokerLogger.LOG.asyncHandlerDuplicate(asyncType, entry.getValue().keySet()); + } + chosen = candidate; + } } - if (existing.getHandlerClass() == handlerClass && existing.isReturnType() != isReturnType) { - throw InvokerLogger.LOG.asyncHandlerBothKinds(handlerClass, asyncType); + if (chosen == null) { + throw InvokerLogger.LOG.invalidAsyncHandlerSelection(asyncType.getName(), selection); } - throw InvokerLogger.LOG.asyncHandlerDuplicate(asyncType, handlerClass); + resolved.put(asyncType, chosen); + unresolved.remove(asyncType.getName()); + } + if (!unresolved.isEmpty()) { + String asyncType = unresolved.iterator().next(); + throw InvokerLogger.LOG.invalidAsyncHandlerSelection(asyncType, selections.get(asyncType)); } + handlers.clear(); + handlers.putAll(resolved); } private Class extractAsyncType(Class handlerClass, Class targetInterface) { @@ -159,7 +198,7 @@ public boolean hasHandler(Class asyncType) { /** * Finds a matching ReturnType handler for the given method return type. */ - public HandlerInfo findReturnTypeHandler(Class returnType) { + private HandlerInfo findReturnTypeHandler(Class returnType) { HandlerInfo info = handlers.get(returnType); if (info != null && info.isReturnType()) { return info; @@ -168,27 +207,34 @@ public HandlerInfo findReturnTypeHandler(Class returnType) { } /** - * Finds a matching ParameterType handler for the given method parameter types. - * Returns the handler info if exactly one parameter matches; null otherwise. + * Selects a handler only when exactly one return or parameter type handler matches. */ - public HandlerInfo findParameterTypeHandler(Class[] parameterTypes) { - HandlerInfo match = null; - int matchCount = 0; + public HandlerInfo findHandler(Class returnType, Class[] parameterTypes) { + return selectHandler(findReturnTypeHandler(returnType), parameterTypes); + } + + private HandlerInfo selectHandler(HandlerInfo match, Class[] parameterTypes) { + Map, Integer> occurrences = new HashMap<>(); for (Class paramType : parameterTypes) { - HandlerInfo info = handlers.get(paramType); - if (info != null && !info.isReturnType()) { + occurrences.merge(paramType, 1, Integer::sum); + } + for (Map.Entry, Integer> entry : occurrences.entrySet()) { + HandlerInfo info = handlers.get(entry.getKey()); + if (entry.getValue() == 1 && info != null && !info.isReturnType()) { + if (match != null) { + return null; + } match = info; - matchCount++; } } - // spec requires exactly one matching parameter; with 0 or 2+ matches - // the method is not considered async and null signals synchronous cleanup - return matchCount == 1 ? match : null; + return match; } @Override public void cleanup() { handlers.clear(); + candidates.clear(); + selections.clear(); } /** @@ -200,7 +246,6 @@ public static class HandlerInfo { private final Class asyncType; private final Class handlerClass; private final boolean isReturnType; - private boolean builtin; static HandlerInfo returnType(AsyncHandler.ReturnType handler, Class asyncType) { return new HandlerInfo(handler, null, asyncType, handler.getClass(), true); @@ -242,13 +287,6 @@ public boolean isReturnType() { return isReturnType; } - boolean isBuiltin() { - return builtin; - } - - void setBuiltin(boolean builtin) { - this.builtin = builtin; - } } // --- Built-in handlers --- diff --git a/impl/src/main/java/org/jboss/weld/logging/BootstrapLogger.java b/impl/src/main/java/org/jboss/weld/logging/BootstrapLogger.java index d81fd895a3d..b5686593e6a 100644 --- a/impl/src/main/java/org/jboss/weld/logging/BootstrapLogger.java +++ b/impl/src/main/java/org/jboss/weld/logging/BootstrapLogger.java @@ -201,9 +201,7 @@ public interface BootstrapLogger extends WeldLogger { @Message(id = 145, value = "Bean identifier index built:\n {0}", format = Format.MESSAGE_FORMAT) void beanIdentifierIndexBuilt(Object info); - @LogMessage(level = Level.WARN) - @Message(id = 146, value = "BeforeBeanDiscovery.addAnnotatedType(AnnotatedType) used for {0} is deprecated from CDI 1.1!", format = Format.MESSAGE_FORMAT) - void deprecatedAddAnnotatedTypeMethodUsed(Class clazz); + // Message id 146 is reserved for the removed deprecated addAnnotatedType warning. @LogMessage(level = Level.WARN) @Message(id = 147, value = "Decorator {0} declares inappropriate constructor therefore will not available as a managed bean!", format = Format.MESSAGE_FORMAT) diff --git a/impl/src/main/java/org/jboss/weld/logging/InvokerLogger.java b/impl/src/main/java/org/jboss/weld/logging/InvokerLogger.java index 4356b4e78d4..b9072042728 100644 --- a/impl/src/main/java/org/jboss/weld/logging/InvokerLogger.java +++ b/impl/src/main/java/org/jboss/weld/logging/InvokerLogger.java @@ -115,6 +115,12 @@ public interface InvokerLogger extends WeldLogger { @Message(id = 2029, value = "Unhandled primitive type: {0}", format = Format.MESSAGE_FORMAT) RuntimeException unhandledPrimitiveType(Object primitive); - @Message(id = 2030, value = "AsyncHandler {0} implements both ReturnType and ParameterType for the same async type {1}", format = Format.MESSAGE_FORMAT) - DefinitionException asyncHandlerBothKinds(Object handlerClass, Object asyncType); + @Message(id = 2030, value = "AsyncHandler {0} implements both ReturnType and ParameterType", format = Format.MESSAGE_FORMAT) + DefinitionException asyncHandlerBothKinds(Object handlerClass); + + @Message(id = 2031, value = "Configured async handler {1} is not registered for async type {0}", format = Format.MESSAGE_FORMAT) + DeploymentException invalidAsyncHandlerSelection(Object asyncType, Object handlerClass); + + @Message(id = 2032, value = "Invalid async handler configuration {0}; expected comma-separated asyncType=handlerClass mappings with unique async types", format = Format.MESSAGE_FORMAT) + DeploymentException invalidAsyncHandlerConfiguration(Object configuration); } diff --git a/impl/src/main/java/org/jboss/weld/util/ForwardingWeldInstance.java b/impl/src/main/java/org/jboss/weld/util/ForwardingWeldInstance.java index 7210130392b..79d39c694de 100644 --- a/impl/src/main/java/org/jboss/weld/util/ForwardingWeldInstance.java +++ b/impl/src/main/java/org/jboss/weld/util/ForwardingWeldInstance.java @@ -75,17 +75,7 @@ public void destroy(T instance) { } @Override - public WeldInstance.Handler getHandler() { - return delegate().getHandler(); - } - - @Override - public Iterable> handlers() { - return delegate().handlers(); - } - - @Override - public Handle getHandle() { + public Handle getHandle() { return delegate().getHandle(); } @@ -94,11 +84,6 @@ public Iterable> handles() { return delegate().handles(); } - @Override - public Comparator> getPriorityComparator() { - return delegate().getPriorityComparator(); - } - @Override public Comparator> getHandlePriorityComparator() { return delegate().getHandlePriorityComparator(); diff --git a/impl/src/test/java/org/jboss/weld/invokable/AsyncHandlerRegistryTest.java b/impl/src/test/java/org/jboss/weld/invokable/AsyncHandlerRegistryTest.java new file mode 100644 index 00000000000..df18c75c127 --- /dev/null +++ b/impl/src/test/java/org/jboss/weld/invokable/AsyncHandlerRegistryTest.java @@ -0,0 +1,210 @@ +package org.jboss.weld.invokable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import java.net.URL; +import java.nio.file.Files; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import jakarta.enterprise.inject.spi.DefinitionException; +import jakarta.enterprise.inject.spi.DeploymentException; +import jakarta.enterprise.invoke.AsyncHandler; + +import org.jboss.weld.resources.DefaultResourceLoader; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class AsyncHandlerRegistryTest { + @Rule + public TemporaryFolder files = new TemporaryFolder(); + + private void discover(AsyncHandlerRegistry registry, Class... providers) throws Exception { + discoverService(registry, AsyncHandler.ReturnType.class, providers); + } + + private void discoverService(AsyncHandlerRegistry registry, Class service, Class... providers) throws Exception { + var file = files.newFile().toPath(); + Files.writeString(file, Stream.of(providers).map(Class::getName).collect(Collectors.joining("\n"))); + URL descriptor = file.toUri().toURL(); + registry.discoverHandlers(new DefaultResourceLoader() { + @Override + public Collection getResources(String name) { + return name.equals("META-INF/services/" + service.getName()) + ? List.of(descriptor) + : List.of(); + } + }); + } + + @Test + public void builtinCollisionRequiresConfiguration() throws Exception { + var registry = new AsyncHandlerRegistry(); + discover(registry, StageHandler.class); + assertThrows(DeploymentException.class, registry::validateHandlers); + } + + @Test + public void selectsCustomBuiltinReplacement() throws Exception { + var registry = new AsyncHandlerRegistry(CompletionStage.class.getName() + "=" + StageHandler.class.getName()); + discover(registry, StageHandler.class); + registry.validateHandlers(); + assertEquals(StageHandler.class, registry.getHandler(CompletionStage.class).getHandlerClass()); + } + + @Test + public void selectsBuiltinOverCustomProvider() throws Exception { + var registry = new AsyncHandlerRegistry(CompletionStage.class.getName() + "=" + + AsyncHandlerRegistry.BuiltinCompletionStageHandler.class.getName()); + discover(registry, StageHandler.class); + registry.validateHandlers(); + assertEquals(AsyncHandlerRegistry.BuiltinCompletionStageHandler.class, + registry.getHandler(CompletionStage.class).getHandlerClass()); + assertTrue(registry.getHandler(CompletionStage.class).isReturnType()); + } + + @Test + public void selectsMultipleHandlersWithWhitespace() throws Exception { + var registry = new AsyncHandlerRegistry(" \t" + String.class.getName() + " = " + + SecondStringHandler.class.getName() + " , " + Integer.class.getName() + " = " + + IntegerHandler.class.getName() + " \t"); + discover(registry, StringHandler.class, SecondStringHandler.class); + discoverService(registry, AsyncHandler.ParameterType.class, IntegerHandler.class, SecondIntegerHandler.class); + registry.validateHandlers(); + assertEquals(SecondStringHandler.class, registry.getHandler(String.class).getHandlerClass()); + assertTrue(registry.getHandler(String.class).isReturnType()); + assertEquals(IntegerHandler.class, registry.getHandler(Integer.class).getHandlerClass()); + assertFalse(registry.getHandler(Integer.class).isReturnType()); + } + + @Test + public void selectsOneOfDuplicateCustomProvidersAfterAllArchives() throws Exception { + var registry = new AsyncHandlerRegistry(String.class.getName() + "=" + SecondStringHandler.class.getName()); + discover(registry, StringHandler.class); + discover(registry, SecondStringHandler.class); + registry.validateHandlers(); + assertEquals(SecondStringHandler.class, registry.getHandler(String.class).getHandlerClass()); + } + + @Test + public void duplicateCustomProvidersFailByDefault() throws Exception { + var registry = new AsyncHandlerRegistry(); + discover(registry, StringHandler.class, SecondStringHandler.class); + assertThrows(DeploymentException.class, registry::validateHandlers); + } + + @Test + public void repeatedDiscoveryIsNotADuplicate() throws Exception { + var registry = new AsyncHandlerRegistry(); + discover(registry, StringHandler.class); + discover(registry, StringHandler.class); + registry.validateHandlers(); + assertEquals(StringHandler.class, registry.getHandler(String.class).getHandlerClass()); + } + + @Test + public void missingConfiguredProviderFails() { + var registry = new AsyncHandlerRegistry(CompletionStage.class.getName() + "=missing.Handler"); + var failure = assertThrows(DeploymentException.class, registry::validateHandlers); + assertTrue(failure.getMessage().contains("WELD-002031")); + } + + @Test + public void wrongConfiguredAsyncTypeFails() throws Exception { + var registry = new AsyncHandlerRegistry(CompletionStage.class.getName() + "=" + StringHandler.class.getName()); + discover(registry, StringHandler.class); + assertThrows(DeploymentException.class, registry::validateHandlers); + } + + @Test + public void undiscoveredAsyncTypeFails() { + var registry = new AsyncHandlerRegistry("missing.Type=missing.Handler"); + assertThrows(DeploymentException.class, registry::validateHandlers); + } + + @Test + public void malformedAndDuplicateMappingsFail() { + for (String configuration : List.of("type", "=handler", "type=", "type=one,type=two", "type=handler,", + "type=handler=extra", ",type=handler", "type=handler,,other=provider", " \t=handler", "type= \t", + "type=one, type =two")) { + var failure = assertThrows(configuration, DeploymentException.class, () -> new AsyncHandlerRegistry(configuration)); + assertTrue(configuration, failure.getMessage().contains("WELD-002032")); + } + } + + @Test + public void bothKindsWithDifferentTypesFailThroughEitherDescriptor() { + for (Class service : List.of(AsyncHandler.ReturnType.class, AsyncHandler.ParameterType.class)) { + assertThrows(DefinitionException.class, + () -> discoverService(new AsyncHandlerRegistry(), service, BothKinds.class)); + } + } + + @Test + public void bothKindsWithSameTypeFailThroughSingleDescriptor() { + assertThrows(DefinitionException.class, () -> discover(new AsyncHandlerRegistry(), BothKindsSameType.class)); + } + + public static class BothKinds implements AsyncHandler.ReturnType, AsyncHandler.ParameterType { + public String transform(String original, Runnable completion) { + return original; + } + + public Integer transformArgument(Integer original, Runnable completion) { + return original; + } + } + + public static class BothKindsSameType implements AsyncHandler.ReturnType, AsyncHandler.ParameterType { + public String transform(String original, Runnable completion) { + return original; + } + + public String transformArgument(String original, Runnable completion) { + return original; + } + } + + public static class StageHandler implements AsyncHandler.ReturnType> { + public CompletionStage transform(CompletionStage original, Runnable completion) { + return original.whenComplete((value, failure) -> completion.run()); + } + } + + public static class IntegerHandler implements AsyncHandler.ParameterType { + @Override + public Integer transformArgument(Integer original, Runnable completion) { + completion.run(); + return original; + } + } + + public static class SecondIntegerHandler implements AsyncHandler.ParameterType { + @Override + public Integer transformArgument(Integer original, Runnable completion) { + completion.run(); + return original; + } + } + + public static class StringHandler implements AsyncHandler.ReturnType { + public String transform(String original, Runnable completion) { + completion.run(); + return original; + } + } + + public static class SecondStringHandler implements AsyncHandler.ReturnType { + public String transform(String original, Runnable completion) { + completion.run(); + return original; + } + } +} diff --git a/pom.xml b/pom.xml index 9fc4b603c2f..711e8aa30c8 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 17 17 - + 17 @@ -35,7 +35,7 @@ ${maven.build.timestamp} - + 4.0.3 @@ -60,9 +60,10 @@ 1.0.2.Final 1.2.5.Final 2.0.1 - + 5.0.0 - + + 11.0.3 @@ -77,6 +78,7 @@ 6.0.1 5.0.0-M1 3.1.0 + 4.0.0 4.1.2 3.6.3.Final 3.0.4.Final @@ -90,7 +92,8 @@ 4.10.4.1 4.10.4 7.12.0 - 7.0.CR1 + 3.1.1 + 7.0.CR2 5.1.0.Final @@ -298,6 +301,18 @@ pom + + jakarta.validation + jakarta.validation-api + ${validation.api.version} + + + + jakarta.ws.rs + jakarta.ws.rs-api + ${jaxrs.api.version} + + org.jboss.weld weld-core-impl diff --git a/porting-package/pom.xml b/porting-package/pom.xml index f666c3699d6..26fecfbbb51 100644 --- a/porting-package/pom.xml +++ b/porting-package/pom.xml @@ -53,12 +53,6 @@ - - org.jboss.spec.javax.servlet - jboss-servlet-api_4.0_spec - 2.0.0.Final - - diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/bootstrap/unusedbeans/rest/JakartaRestUnusedBeanTest.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/bootstrap/unusedbeans/rest/JakartaRestUnusedBeanTest.java new file mode 100644 index 00000000000..765d704f55c --- /dev/null +++ b/tests-arquillian/src/test/java/org/jboss/weld/tests/bootstrap/unusedbeans/rest/JakartaRestUnusedBeanTest.java @@ -0,0 +1,74 @@ +package org.jboss.weld.tests.bootstrap.unusedbeans.rest; + +import static org.junit.Assert.assertEquals; + +import java.util.Map; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.BeanArchive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.weld.bootstrap.api.Service; +import org.jboss.weld.config.ConfigurationKey; +import org.jboss.weld.configuration.spi.ExternalConfiguration; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(Arquillian.class) +public class JakartaRestUnusedBeanTest { + @Deployment + public static Archive deploy() { + // WildFly supplies its own ExternalConfiguration, so set excludeType through weld.properties. + // CleanupConfiguration enables integrator-only optimized cleanup for the embedded runner; + // WildFly already enables it in its own configuration. + return ShrinkWrap.create(BeanArchive.class) + .addClasses(JakartaRestUnusedBeanTest.class.getDeclaredClasses()) + .addAsServiceProvider(Service.class, CleanupConfiguration.class) + .addAsResource(new StringAsset(ConfigurationKey.UNUSED_BEANS_EXCLUDE_TYPE.get() + "=" + + ConfigurationKey.UnusedBeans.NONE), "weld.properties"); + } + + @Inject + BeanManager manager; + + @Test + public void defaultAnnotationPatternProtectsJakartaRestBeans() { + assertEquals(1, manager.getBeans(RestResource.class).size()); + assertEquals(1, manager.getBeans(RestMethod.class).size()); + assertEquals(0, manager.getBeans(Unused.class).size()); + } + + @ApplicationScoped + @Path("/resource") + public static class RestResource { + } + + @ApplicationScoped + public static class RestMethod { + @GET + public String get() { + return "value"; + } + } + + @ApplicationScoped + public static class Unused { + } + + public static class CleanupConfiguration implements ExternalConfiguration { + public Map getConfigurationProperties() { + return Map.of(ConfigurationKey.ALLOW_OPTIMIZED_CLEANUP.get(), true); + } + + public void cleanup() { + } + } +} diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/instance/enhanced/InstanceHandleTest.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/instance/enhanced/InstanceHandleTest.java index 13fe4d8c803..9c8e93a51e5 100644 --- a/tests-arquillian/src/test/java/org/jboss/weld/tests/instance/enhanced/InstanceHandleTest.java +++ b/tests-arquillian/src/test/java/org/jboss/weld/tests/instance/enhanced/InstanceHandleTest.java @@ -49,7 +49,7 @@ public static Archive createTestArchive() { return ShrinkWrap.create(BeanArchive.class, Utils.getDeploymentNameAsHash(InstanceHandleTest.class)) .addClasses(Alpha.class, Bravo.class, Client.class, FirstProcessor.class, Processor.class, SecondProcessor.class, Juicy.class) - .addPackage(WeldInstanceTest.class.getPackage()) + .addPackage(InstanceHandleTest.class.getPackage()) .addClass(ActionSequence.class); } diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/instance/enhanced/WeldClient.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/instance/enhanced/WeldClient.java deleted file mode 100644 index 79eb974be51..00000000000 --- a/tests-arquillian/src/test/java/org/jboss/weld/tests/instance/enhanced/WeldClient.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * JBoss, Home of Professional Open Source - * Copyright 2016, Red Hat, Inc., and individual contributors - * by the @authors tag. See the copyright.txt in the distribution for a - * full listing of individual contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jboss.weld.tests.instance.enhanced; - -import java.math.BigDecimal; - -import jakarta.enterprise.context.Dependent; -import jakarta.inject.Inject; - -import org.jboss.weld.inject.WeldInstance; - -/** - * A version of {@link Client} that uses {@link WeldInstance} interface - * - * @author Matej Novotny - */ -@Dependent -public class WeldClient { - - @Inject - WeldInstance alphaInstance; - - @Inject - WeldInstance instance; - - @Inject - @Juicy - WeldInstance bigDecimalInstance; - - WeldInstance getAlphaInstance() { - return alphaInstance; - } - - WeldInstance getBigDecimalInstance() { - return bigDecimalInstance; - } - - WeldInstance getInstance() { - return instance; - } -} diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/instance/enhanced/WeldInstanceTest.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/instance/enhanced/WeldInstanceTest.java deleted file mode 100644 index a18de5eef09..00000000000 --- a/tests-arquillian/src/test/java/org/jboss/weld/tests/instance/enhanced/WeldInstanceTest.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * JBoss, Home of Professional Open Source - * Copyright 2016, Red Hat, Inc., and individual contributors - * by the @authors tag. See the copyright.txt in the distribution for a - * full listing of individual contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jboss.weld.tests.instance.enhanced; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.util.Iterator; -import java.util.List; - -import jakarta.enterprise.context.Dependent; -import jakarta.enterprise.inject.spi.Bean; -import jakarta.enterprise.inject.spi.BeanManager; - -import org.jboss.arquillian.container.test.api.Deployment; -import org.jboss.arquillian.junit.Arquillian; -import org.jboss.shrinkwrap.api.Archive; -import org.jboss.shrinkwrap.api.BeanArchive; -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.weld.inject.WeldInstance; -import org.jboss.weld.inject.WeldInstance.Handler; -import org.jboss.weld.test.util.ActionSequence; -import org.jboss.weld.test.util.Utils; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * - * @author Matej Novotny - */ -@RunWith(Arquillian.class) -public class WeldInstanceTest { - - @Deployment - public static Archive createTestArchive() { - return ShrinkWrap.create(BeanArchive.class, Utils.getDeploymentNameAsHash(WeldInstanceTest.class)) - .addClasses(Alpha.class, Bravo.class, FirstProcessor.class, Juicy.class, Processor.class, - SecondProcessor.class, WeldClient.class) - .addClass(ActionSequence.class); - } - - @Test - public void testIsResolvable(WeldClient client) { - ActionSequence.reset(); - assertNotNull(client); - assertTrue(client.getAlphaInstance().isResolvable()); - assertFalse(client.getBigDecimalInstance().isResolvable()); - } - - @Test - public void testGetHandler(WeldClient client, BeanManager beanManager) { - ActionSequence.reset(); - assertNotNull(client); - - Bean alphaBean = beanManager.resolve(beanManager.getBeans(Alpha.class)); - WeldInstance instance = client.getAlphaInstance(); - - Handler alpha1 = instance.getHandler(); - assertEquals(alphaBean, alpha1.getBean()); - assertEquals(Dependent.class, alpha1.getBean().getScope()); - - String alpha2Id; - - // Test try-with-resource - try (Handler alpha2 = instance.getHandler()) { - alpha2Id = alpha2.get().getId(); - assertFalse(alpha1.get().getId().equals(alpha2Id)); - } - - List sequence = ActionSequence.getSequenceData(); - assertEquals(1, sequence.size()); - assertEquals(alpha2Id, sequence.get(0)); - - alpha1.destroy(); - // Subsequent invocations are no-op - alpha1.destroy(); - - sequence = ActionSequence.getSequenceData(); - assertEquals(2, sequence.size()); - - // Test normal scoped bean is also destroyed - WeldInstance bravoInstance = client.getInstance().select(Bravo.class); - String bravoId = bravoInstance.get().getId(); - try (Handler bravo = bravoInstance.getHandler()) { - assertEquals(bravoId, bravo.get().getId()); - ActionSequence.reset(); - } - sequence = ActionSequence.getSequenceData(); - assertEquals(1, sequence.size()); - assertEquals(bravoId, sequence.get(0)); - } - - @Test - public void testGetAfterDestroyingContextualInstance(WeldClient client) { - ActionSequence.reset(); - assertNotNull(client); - - Handler alphaHandle = client.getAlphaInstance().getHandler(); - // trigger bean creation - alphaHandle.get(); - // trigger bean destruction - alphaHandle.destroy(); - // verify that the destruction happened - List sequence = ActionSequence.getSequenceData(); - assertEquals(1, sequence.size()); - - // try to invoke Handle.get() again; this should throw an exception - try { - alphaHandle.get(); - Assert.fail("Invoking Handle.get() after destroying contextual instance should throw an exception."); - } catch (IllegalStateException e) { - // expected - } - } - - @Test - public void testHandlers(WeldInstance instance) { - ActionSequence.reset(); - assertTrue(instance.isAmbiguous()); - for (Handler handler : instance.handlers()) { - handler.get().ping(); - if (handler.getBean().getScope().equals(Dependent.class)) { - handler.destroy(); - } - } - assertEquals(3, ActionSequence.getSequenceSize()); - ActionSequence.assertSequenceDataContainsAll("firstPing", "secondPing", "firstDestroy"); - - ActionSequence.reset(); - assertTrue(instance.isAmbiguous()); - for (Iterator> iterator = instance.handlers().iterator(); iterator.hasNext();) { - try (Handler handler = iterator.next()) { - handler.get().ping(); - } - } - assertEquals(4, ActionSequence.getSequenceSize()); - ActionSequence.assertSequenceDataContainsAll("firstPing", "secondPing", "firstDestroy", "secondDestroy"); - } -} diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/AroundConstructInterceptor.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/AroundConstructInterceptor.java deleted file mode 100644 index e3c9ed710db..00000000000 --- a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/AroundConstructInterceptor.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * JBoss, Home of Professional Open Source - * Copyright 2013, Red Hat, Inc., and individual contributors - * by the @authors tag. See the copyright.txt in the distribution for a - * full listing of individual contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jboss.weld.tests.interceptors.context.bindings; - -import java.lang.annotation.Annotation; -import java.util.Set; - -import jakarta.annotation.Priority; -import jakarta.interceptor.AroundConstruct; -import jakarta.interceptor.Interceptor; -import jakarta.interceptor.InvocationContext; - -import org.jboss.weld.interceptor.WeldInvocationContext; - -@Priority(value = Interceptor.Priority.APPLICATION) -@Interceptor -@FooBinding(secret = "nonbinding") -public class AroundConstructInterceptor { - - private static Set contextDataBindings; - - private static Set contextBindings; - - @SuppressWarnings("unchecked") - @AroundConstruct - void intercept(InvocationContext ctx) throws Exception { - contextDataBindings = (Set) ctx.getContextData().get(WeldInvocationContext.INTERCEPTOR_BINDINGS_KEY); - if (ctx instanceof WeldInvocationContext) { - contextBindings = ((WeldInvocationContext) ctx).getInterceptorBindings(); - } - ctx.proceed(); - } - - static void reset() { - contextDataBindings = null; - contextBindings = null; - } - - static Set getContextDataBindings() { - return contextDataBindings; - } - - static Set getContextBindings() { - return contextBindings; - } -} \ No newline at end of file diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/AroundInvokeInterceptor.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/AroundInvokeInterceptor.java deleted file mode 100644 index cd4253fb1a6..00000000000 --- a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/AroundInvokeInterceptor.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * JBoss, Home of Professional Open Source - * Copyright 2013, Red Hat, Inc., and individual contributors - * by the @authors tag. See the copyright.txt in the distribution for a - * full listing of individual contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jboss.weld.tests.interceptors.context.bindings; - -import java.lang.annotation.Annotation; -import java.util.Set; - -import jakarta.annotation.Priority; -import jakarta.interceptor.AroundInvoke; -import jakarta.interceptor.Interceptor; -import jakarta.interceptor.InvocationContext; - -import org.jboss.weld.interceptor.WeldInvocationContext; - -@Priority(value = Interceptor.Priority.APPLICATION) -@Interceptor -@FooBinding(secret = "nonbinding") -@BarBinding(age = 1) -public class AroundInvokeInterceptor { - - private static final String KEY = "org.jboss.weld.interceptor.bindings"; - - private static Set contextDataBindings; - - private static Set contextBindings; - - @SuppressWarnings("unchecked") - @AroundInvoke - Object intercept(InvocationContext ctx) throws Exception { - contextDataBindings = (Set) ctx.getContextData().get(KEY); - if (ctx instanceof WeldInvocationContext) { - contextBindings = ((WeldInvocationContext) ctx).getInterceptorBindings(); - } - return ctx.proceed(); - } - - static void reset() { - contextDataBindings = null; - contextBindings = null; - } - - static Set getContextDataBindings() { - return contextDataBindings; - } - - static Set getContextBindings() { - return contextBindings; - } - -} diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/BarBinding.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/BarBinding.java deleted file mode 100644 index a20d5f257a0..00000000000 --- a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/BarBinding.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * JBoss, Home of Professional Open Source - * Copyright 2013, Red Hat, Inc., and individual contributors - * by the @authors tag. See the copyright.txt in the distribution for a - * full listing of individual contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jboss.weld.tests.interceptors.context.bindings; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import jakarta.interceptor.InterceptorBinding; - -@InterceptorBinding -@Inherited -@Target({ TYPE, METHOD }) -@Retention(RUNTIME) -public @interface BarBinding { - - int age() default 0; - -} diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/BazBinding.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/BazBinding.java deleted file mode 100644 index 95974ef26f3..00000000000 --- a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/BazBinding.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * JBoss, Home of Professional Open Source - * Copyright 2013, Red Hat, Inc., and individual contributors - * by the @authors tag. See the copyright.txt in the distribution for a - * full listing of individual contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jboss.weld.tests.interceptors.context.bindings; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import jakarta.enterprise.util.Nonbinding; -import jakarta.interceptor.InterceptorBinding; - -@InterceptorBinding -@Inherited -@Target({ TYPE }) -@Retention(RUNTIME) -public @interface BazBinding { - - @Nonbinding - String secret() default ""; - -} diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/FooBinding.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/FooBinding.java deleted file mode 100644 index bc120c42cc5..00000000000 --- a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/FooBinding.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * JBoss, Home of Professional Open Source - * Copyright 2013, Red Hat, Inc., and individual contributors - * by the @authors tag. See the copyright.txt in the distribution for a - * full listing of individual contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jboss.weld.tests.interceptors.context.bindings; - -import static java.lang.annotation.ElementType.CONSTRUCTOR; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import jakarta.enterprise.util.Nonbinding; -import jakarta.interceptor.InterceptorBinding; - -@InterceptorBinding -@Inherited -@Target({ TYPE, METHOD, CONSTRUCTOR }) -@Retention(RUNTIME) -public @interface FooBinding { - - @Nonbinding - String secret() default ""; - -} diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/InvocationContextInterceptorBindingsTest.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/InvocationContextInterceptorBindingsTest.java deleted file mode 100644 index c78fc2b398b..00000000000 --- a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/InvocationContextInterceptorBindingsTest.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * JBoss, Home of Professional Open Source - * Copyright 2013, Red Hat, Inc., and individual contributors - * by the @authors tag. See the copyright.txt in the distribution for a - * full listing of individual contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jboss.weld.tests.interceptors.context.bindings; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; - -import java.lang.annotation.Annotation; -import java.util.Set; - -import jakarta.enterprise.inject.Instance; - -import org.jboss.arquillian.container.test.api.Deployment; -import org.jboss.arquillian.junit.Arquillian; -import org.jboss.shrinkwrap.api.Archive; -import org.jboss.shrinkwrap.api.BeanArchive; -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.weld.test.util.Utils; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests now deprecated functionality, can be altered/removed once we no longer provide - * {@link org.jboss.weld.interceptor.WeldInvocationContext#INTERCEPTOR_BINDINGS_KEY} - *

- * See https://issues.redhat.com/browse/WELD-2756 - */ -@RunWith(Arquillian.class) -public class InvocationContextInterceptorBindingsTest { - - @Deployment - public static Archive getDeployment() { - return ShrinkWrap - .create(BeanArchive.class, Utils.getDeploymentNameAsHash(InvocationContextInterceptorBindingsTest.class)) - .addPackage(InvocationContextInterceptorBindingsTest.class.getPackage()); - } - - @Test - public void testAroundInvokeBindings(SimpleBean bean) { - AroundInvokeInterceptor.reset(); - bean.ping(); - Set bindings = AroundInvokeInterceptor.getContextDataBindings(); - assertEquals(bindings, AroundInvokeInterceptor.getContextBindings()); - assertNotNull(bindings); - assertEquals(3, bindings.size()); - for (Annotation annotation : bindings) { - if (annotation.annotationType().equals(BarBinding.class)) { - BarBinding barBinding = (BarBinding) annotation; - assertEquals("1", "" + barBinding.age()); - } else if (annotation.annotationType().equals(FooBinding.class)) { - FooBinding fooBinding = (FooBinding) annotation; - assertEquals("hello", fooBinding.secret()); - } else if (annotation.annotationType().equals(BazBinding.class)) { - BazBinding bazBinding = (BazBinding) annotation; - assertEquals("bye", bazBinding.secret()); - } else { - fail("Unexpected annotation type"); - } - } - } - - @Test - public void testAroundConstructBindings(Instance instance) { - AroundConstructInterceptor.reset(); - instance.get().ping(); - Set bindings = AroundConstructInterceptor.getContextDataBindings(); - assertEquals(bindings, AroundConstructInterceptor.getContextBindings()); - assertNotNull(bindings); - assertEquals(2, bindings.size()); - for (Annotation annotation : bindings) { - if (annotation.annotationType().equals(FooBinding.class)) { - FooBinding fooBinding = (FooBinding) annotation; - assertEquals("bla", fooBinding.secret()); - } else if (annotation.annotationType().equals(BazBinding.class)) { - BazBinding bazBinding = (BazBinding) annotation; - assertEquals("bye", bazBinding.secret()); - } else { - fail("Unexpected annotation type"); - } - } - } - - @Test - public void testPostConstructBindings(Instance instance) { - PostConstructInterceptor.reset(); - instance.get().ping(); - Set bindings = PostConstructInterceptor.getContextDataBindings(); - assertEquals(bindings, PostConstructInterceptor.getContextBindings()); - assertNotNull(bindings); - assertEquals(2, bindings.size()); - for (Annotation annotation : bindings) { - if (annotation.annotationType().equals(FooBinding.class)) { - FooBinding fooBinding = (FooBinding) annotation; - assertEquals("hello", fooBinding.secret()); - } else if (annotation.annotationType().equals(BazBinding.class)) { - BazBinding bazBinding = (BazBinding) annotation; - assertEquals("bye", bazBinding.secret()); - } else { - fail("Unexpected annotation type"); - } - } - } - - @Test - public void testPreDestroyBindings(Instance instance) { - PreDestroyInterceptor.reset(); - instance.get().ping(); - Set bindings = PreDestroyInterceptor.getContextDataBindings(); - assertEquals(bindings, PreDestroyInterceptor.getContextBindings()); - assertNotNull(bindings); - assertEquals(2, bindings.size()); - for (Annotation annotation : bindings) { - if (annotation.annotationType().equals(FooBinding.class)) { - FooBinding fooBinding = (FooBinding) annotation; - assertEquals("hello", fooBinding.secret()); - } else if (annotation.annotationType().equals(BazBinding.class)) { - BazBinding bazBinding = (BazBinding) annotation; - assertEquals("bye", bazBinding.secret()); - } else { - fail("Unexpected annotation type"); - } - } - } -} diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/PostConstructInterceptor.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/PostConstructInterceptor.java deleted file mode 100644 index f6e95265291..00000000000 --- a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/PostConstructInterceptor.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * JBoss, Home of Professional Open Source - * Copyright 2013, Red Hat, Inc., and individual contributors - * by the @authors tag. See the copyright.txt in the distribution for a - * full listing of individual contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jboss.weld.tests.interceptors.context.bindings; - -import java.lang.annotation.Annotation; -import java.util.Set; - -import jakarta.annotation.PostConstruct; -import jakarta.annotation.Priority; -import jakarta.interceptor.Interceptor; -import jakarta.interceptor.InvocationContext; - -import org.jboss.weld.interceptor.WeldInvocationContext; - -@Priority(value = Interceptor.Priority.APPLICATION) -@Interceptor -@BazBinding(secret = "nonbinding") -public class PostConstructInterceptor { - - private static Set contextDataBindings; - - private static Set contextBindings; - - @SuppressWarnings("unchecked") - @PostConstruct - void intercept(InvocationContext ctx) throws Exception { - contextDataBindings = (Set) ctx.getContextData().get(WeldInvocationContext.INTERCEPTOR_BINDINGS_KEY); - if (ctx instanceof WeldInvocationContext) { - contextBindings = ((WeldInvocationContext) ctx).getInterceptorBindings(); - } - ctx.proceed(); - } - - static void reset() { - contextDataBindings = null; - contextBindings = null; - } - - static Set getContextDataBindings() { - return contextDataBindings; - } - - static Set getContextBindings() { - return contextBindings; - } -} diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/PreDestroyInterceptor.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/PreDestroyInterceptor.java deleted file mode 100644 index 4dd4c171a48..00000000000 --- a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/PreDestroyInterceptor.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * JBoss, Home of Professional Open Source - * Copyright 2013, Red Hat, Inc., and individual contributors - * by the @authors tag. See the copyright.txt in the distribution for a - * full listing of individual contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jboss.weld.tests.interceptors.context.bindings; - -import java.lang.annotation.Annotation; -import java.util.Set; - -import jakarta.annotation.PostConstruct; -import jakarta.annotation.Priority; -import jakarta.interceptor.Interceptor; -import jakarta.interceptor.InvocationContext; - -import org.jboss.weld.interceptor.WeldInvocationContext; - -@Priority(value = Interceptor.Priority.APPLICATION) -@Interceptor -@BazBinding(secret = "nonbinding") -public class PreDestroyInterceptor { - - private static Set contextDataBindings; - - private static Set contextBindings; - - @SuppressWarnings("unchecked") - @PostConstruct - void intercept(InvocationContext ctx) throws Exception { - contextDataBindings = (Set) ctx.getContextData().get(WeldInvocationContext.INTERCEPTOR_BINDINGS_KEY); - if (ctx instanceof WeldInvocationContext) { - contextBindings = ((WeldInvocationContext) ctx).getInterceptorBindings(); - } - ctx.proceed(); - } - - static void reset() { - contextDataBindings = null; - contextBindings = null; - } - - static Set getContextDataBindings() { - return contextDataBindings; - } - - static Set getContextBindings() { - return contextBindings; - } -} diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/SimpleBean.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/SimpleBean.java deleted file mode 100644 index f1f6506278a..00000000000 --- a/tests-arquillian/src/test/java/org/jboss/weld/tests/interceptors/context/bindings/SimpleBean.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * JBoss, Home of Professional Open Source - * Copyright 2013, Red Hat, Inc., and individual contributors - * by the @authors tag. See the copyright.txt in the distribution for a - * full listing of individual contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jboss.weld.tests.interceptors.context.bindings; - -import jakarta.enterprise.context.Dependent; -import jakarta.inject.Inject; - -@BazBinding(secret = "bye") -@FooBinding(secret = "hello") -@Dependent -public class SimpleBean { - - @FooBinding(secret = "bla") - @Inject - public SimpleBean() { - } - - @BarBinding(age = 1) - void ping() { - } -} diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/invokable/async/matching/AsyncHandlerMatchingTest.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/invokable/async/matching/AsyncHandlerMatchingTest.java new file mode 100644 index 00000000000..5771dd9bcef --- /dev/null +++ b/tests-arquillian/src/test/java/org/jboss/weld/tests/invokable/async/matching/AsyncHandlerMatchingTest.java @@ -0,0 +1,148 @@ +package org.jboss.weld.tests.invokable.async.matching; + +import static org.junit.Assert.assertEquals; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +import jakarta.annotation.PreDestroy; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.spi.Extension; +import jakarta.enterprise.invoke.AsyncHandler; +import jakarta.enterprise.invoke.Invoker; +import jakarta.inject.Inject; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.BeanArchive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.weld.bootstrap.event.WeldProcessManagedBean; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(Arquillian.class) +public class AsyncHandlerMatchingTest { + @Deployment + public static Archive deploy() { + return ShrinkWrap.create(BeanArchive.class) + .addClasses(AsyncHandlerMatchingTest.class.getDeclaredClasses()) + .addAsServiceProvider(Extension.class, Invokers.class) + .addAsServiceProvider(AsyncHandler.ParameterType.class, AHandler.class, BHandler.class); + } + + @Inject + Invokers invokers; + + @Test + public void returnAndParameterHandlersAreAmbiguous() throws Exception { + Target.destroyed = 0; + A a = new A(); + invokers.get("returnAndParameter").invoke(null, new Object[] { a }); + assertEquals(1, Target.destroyed); + assertEquals(null, a.completion); + Target.stage.complete("done"); + assertEquals(1, Target.destroyed); + } + + @Test + public void repeatedTypeDoesNotDisqualifyAnotherHandler() throws Exception { + Target.destroyed = 0; + A a = new A(); + B b = new B(); + invokers.get("repeatedAndUnique").invoke(null, new Object[] { a, a, b }); + assertEquals(0, Target.destroyed); + assertEquals(null, a.completion); + b.completion.run(); + assertEquals(1, Target.destroyed); + } + + @Test + public void distinctParameterHandlersAreAmbiguous() throws Exception { + Target.destroyed = 0; + A a = new A(); + B b = new B(); + invokers.get("distinct").invoke(null, new Object[] { a, b }); + assertEquals(1, Target.destroyed); + assertEquals(null, a.completion); + assertEquals(null, b.completion); + } + + @Test + public void repeatedParametersLeaveReturnHandlerUnambiguous() throws Exception { + Target.destroyed = 0; + A a = new A(); + invokers.get("returnAndRepeated").invoke(null, new Object[] { a, a }); + assertEquals(0, Target.destroyed); + assertEquals(null, a.completion); + Target.stage.complete("done"); + assertEquals(1, Target.destroyed); + } + + public static class A { + Runnable completion; + } + + public static class B { + Runnable completion; + } + + public static class AHandler implements AsyncHandler.ParameterType { + public A transformArgument(A original, Runnable completion) { + original.completion = completion; + return original; + } + } + + public static class BHandler implements AsyncHandler.ParameterType { + public B transformArgument(B original, Runnable completion) { + original.completion = completion; + return original; + } + } + + @Dependent + public static class Target { + static int destroyed; + static CompletableFuture stage; + + public CompletionStage returnAndParameter(A a) { + stage = new CompletableFuture<>(); + return stage; + } + + public void repeatedAndUnique(A a, A another, B b) { + } + + public void distinct(A a, B b) { + } + + public CompletionStage returnAndRepeated(A a, A another) { + stage = new CompletableFuture<>(); + return stage; + } + + @PreDestroy + void destroy() { + destroyed++; + } + } + + public static class Invokers implements Extension { + final Map> methods = new HashMap<>(); + + public Invoker get(String name) { + return methods.get(name); + } + + void register(@Observes WeldProcessManagedBean event) { + event.getAnnotatedBeanClass().getMethods().stream() + .filter(method -> !method.getJavaMember().getName().equals("destroy")) + .forEach(method -> methods.put(method.getJavaMember().getName(), + event.createInvoker(method).withInstanceLookup().build())); + } + } +} diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/invokable/async/selection/BuiltinHandlerCollisionTest.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/invokable/async/selection/BuiltinHandlerCollisionTest.java new file mode 100644 index 00000000000..d7490bbe15f --- /dev/null +++ b/tests-arquillian/src/test/java/org/jboss/weld/tests/invokable/async/selection/BuiltinHandlerCollisionTest.java @@ -0,0 +1,36 @@ +package org.jboss.weld.tests.invokable.async.selection; + +import java.util.concurrent.CompletionStage; + +import jakarta.enterprise.inject.spi.DeploymentException; +import jakarta.enterprise.invoke.AsyncHandler; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.ShouldThrowException; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.BeanArchive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(Arquillian.class) +public class BuiltinHandlerCollisionTest { + @Deployment + @ShouldThrowException(DeploymentException.class) + public static Archive deploy() { + return ShrinkWrap.create(BeanArchive.class) + .addClass(CustomStageHandler.class) + .addAsServiceProvider(AsyncHandler.ReturnType.class, CustomStageHandler.class); + } + + @Test + public void duplicateBuiltinRequiresSelection() { + } + + public static class CustomStageHandler implements AsyncHandler.ReturnType> { + public CompletionStage transform(CompletionStage original, Runnable completion) { + return original.whenComplete((value, failure) -> completion.run()); + } + } +} diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/invokable/async/selection/ConfiguredAsyncHandlerTest.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/invokable/async/selection/ConfiguredAsyncHandlerTest.java new file mode 100644 index 00000000000..b901307a1f2 --- /dev/null +++ b/tests-arquillian/src/test/java/org/jboss/weld/tests/invokable/async/selection/ConfiguredAsyncHandlerTest.java @@ -0,0 +1,71 @@ +package org.jboss.weld.tests.invokable.async.selection; + +import static org.junit.Assert.assertEquals; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.spi.Extension; +import jakarta.enterprise.invoke.AsyncHandler; +import jakarta.enterprise.invoke.Invoker; +import jakarta.inject.Inject; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.BeanArchive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.weld.bootstrap.event.WeldProcessManagedBean; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(Arquillian.class) +public class ConfiguredAsyncHandlerTest { + @Deployment + public static Archive deploy() { + return ShrinkWrap.create(BeanArchive.class) + .addClasses(ConfiguredAsyncHandlerTest.class.getDeclaredClasses()) + .addAsServiceProvider(Extension.class, Invokers.class) + .addAsServiceProvider(AsyncHandler.ReturnType.class, CustomStageHandler.class) + .addAsResource(new StringAsset("org.jboss.weld.invokable.asyncHandlers=" + + CompletionStage.class.getName() + "=" + CustomStageHandler.class.getName()), "weld.properties"); + } + + @Inject + Invokers invokers; + + @Test + public void configuredProviderTransformsInvocationResult() throws Exception { + assertEquals("custom:value", ((CompletionStage) invokers.get().invoke(null, new Object[0])) + .toCompletableFuture().join()); + } + + @Dependent + public static class Target { + public CompletionStage execute() { + return CompletableFuture.completedFuture("value"); + } + } + + public static class CustomStageHandler implements AsyncHandler.ReturnType> { + public CompletionStage transform(CompletionStage original, Runnable completion) { + return original.whenComplete((value, failure) -> completion.run()).thenApply(value -> "custom:" + value); + } + } + + public static class Invokers implements Extension { + private Invoker invoker; + + public Invoker get() { + return invoker; + } + + void register(@Observes WeldProcessManagedBean event) { + event.getAnnotatedBeanClass().getMethods().forEach(method -> invoker = event.createInvoker(method) + .withInstanceLookup().build()); + } + } +} diff --git a/tests-arquillian/src/test/java/org/jboss/weld/tests/invokable/async/wrapper/AsyncInvocationWrapperTest.java b/tests-arquillian/src/test/java/org/jboss/weld/tests/invokable/async/wrapper/AsyncInvocationWrapperTest.java new file mode 100644 index 00000000000..09cc873df13 --- /dev/null +++ b/tests-arquillian/src/test/java/org/jboss/weld/tests/invokable/async/wrapper/AsyncInvocationWrapperTest.java @@ -0,0 +1,187 @@ +package org.jboss.weld.tests.invokable.async.wrapper; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jakarta.annotation.PreDestroy; +import jakarta.enterprise.context.Dependent; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.spi.Extension; +import jakarta.enterprise.invoke.AsyncHandler; +import jakarta.enterprise.invoke.Invoker; +import jakarta.inject.Inject; + +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.BeanArchive; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.weld.bootstrap.event.WeldProcessManagedBean; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(Arquillian.class) +public class AsyncInvocationWrapperTest { + @Deployment + public static Archive deploy() { + return ShrinkWrap.create(BeanArchive.class) + .addClasses(AsyncInvocationWrapperTest.class.getDeclaredClasses()) + .addAsServiceProvider(Extension.class, Invokers.class) + .addAsServiceProvider(AsyncHandler.ParameterType.class, Handler.class) + .addAsServiceProvider(AsyncHandler.ReturnType.class, ReturnHandler.class); + } + + @Inject + Invokers invokers; + + @Test + public void wrappedLookupWaitsForCompletion() throws Exception { + Target.destroyed = 0; + Param param = new Param(); + assertEquals("wrapped:handled:ok", invokers.get("execute").invoke(null, new Object[] { param })); + assertEquals(0, Target.destroyed); + param.completions.get(0).run(); + assertEquals(1, Target.destroyed); + } + + @Test + public void wrapperWorksWithoutLookup() throws Exception { + Target.destroyed = 0; + Param param = new Param(); + assertEquals("wrapped:handled:ok", invokers.get("direct").invoke(new Target(), new Object[] { param })); + param.completions.get(0).run(); + assertEquals(0, Target.destroyed); + } + + @Test + public void synchronousFailureCleansUpOnlyOnce() { + Target.destroyed = 0; + Param param = new Param(); + assertThrows(IllegalArgumentException.class, () -> invokers.get("fail").invoke(null, new Object[] { param })); + assertEquals(1, Target.destroyed); + param.completions.get(0).run(); + assertEquals(1, Target.destroyed); + } + + @Test + public void repeatedDelegationHasIndependentCleanup() throws Exception { + Target.destroyed = 0; + Param param = new Param(); + assertEquals("handled:ok", invokers.get("twice").invoke(null, new Object[] { param })); + assertEquals(2, param.completions.size()); + assertEquals(0, Target.destroyed); + param.completions.get(0).run(); + assertEquals(1, Target.destroyed); + param.completions.get(1).run(); + assertEquals(2, Target.destroyed); + } + + @Test + public void returnHandlerTransformsBeforeWrapperWithoutLookup() throws Exception { + Target.destroyed = 0; + assertEquals("wrapped:handled:ok", invokers.get("returnDirect").invoke(new Target(), new Object[0])); + assertEquals(0, Target.destroyed); + } + + public static class Param { + final List completions = new ArrayList<>(); + } + + public static class Handler implements AsyncHandler.ParameterType { + public Param transformArgument(Param original, Runnable completion) { + original.completions.add(completion); + return original; + } + + @Override + public Object transformReturnValue(Object original, Runnable completion) { + return "handled:" + original; + } + } + + public static class ReturnValue { + final String value; + + ReturnValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + } + + public static class ReturnHandler implements AsyncHandler.ReturnType { + @Override + public ReturnValue transform(ReturnValue original, Runnable completion) { + completion.run(); + return new ReturnValue("handled:" + original.value); + } + } + + public static class Wrapper { + public static Object once(Target instance, Object[] args, Invoker invoker) throws Exception { + return "wrapped:" + invoker.invoke(instance, args); + } + + public static Object twice(Target instance, Object[] args, Invoker invoker) throws Exception { + invoker.invoke(instance, args); + return invoker.invoke(instance, args); + } + } + + @Dependent + public static class Target { + static int destroyed; + + public String execute(Param param) { + return "ok"; + } + + public String fail(Param param) { + throw new IllegalArgumentException("expected"); + } + + public ReturnValue returnValue() { + return new ReturnValue("ok"); + } + + @PreDestroy + void destroy() { + destroyed++; + } + } + + public static class Invokers implements Extension { + final Map> methods = new HashMap<>(); + + public Invoker get(String name) { + return methods.get(name); + } + + void register(@Observes WeldProcessManagedBean event) { + event.getAnnotatedBeanClass().getMethods().forEach(method -> { + String name = method.getJavaMember().getName(); + if (name.equals("destroy")) { + return; + } + methods.put(name, event.createInvoker(method).withInstanceLookup() + .withInvocationWrapper(Wrapper.class, "once").build()); + if (name.equals("execute")) { + methods.put("direct", event.createInvoker(method).withInvocationWrapper(Wrapper.class, "once").build()); + methods.put("twice", event.createInvoker(method).withInstanceLookup() + .withInvocationWrapper(Wrapper.class, "twice").build()); + } else if (name.equals("returnValue")) { + methods.put("returnDirect", event.createInvoker(method) + .withInvocationWrapper(Wrapper.class, "once").build()); + } + }); + } + } +} diff --git a/weld-lite-extension-translator/src/main/java/org/jboss/weld/lite/extension/translator/LiteExtensionTranslator.java b/weld-lite-extension-translator/src/main/java/org/jboss/weld/lite/extension/translator/LiteExtensionTranslator.java index 841866f454d..627dc84d24d 100644 --- a/weld-lite-extension-translator/src/main/java/org/jboss/weld/lite/extension/translator/LiteExtensionTranslator.java +++ b/weld-lite-extension-translator/src/main/java/org/jboss/weld/lite/extension/translator/LiteExtensionTranslator.java @@ -51,15 +51,6 @@ public LiteExtensionTranslator() { this(BuildCompatibleExtensionLoader.getBuildCompatibleExtensions(), Thread.currentThread().getContextClassLoader()); } - /** - * Deprecated, use {@link LiteExtensionTranslator#LiteExtensionTranslator(Collection, ClassLoader)}. - * This method will be removed in future versions. - */ - @Deprecated(forRemoval = true) - public LiteExtensionTranslator(List> buildCompatibleExtensions, ClassLoader cl) { - this((Collection>) buildCompatibleExtensions, cl); - } - public LiteExtensionTranslator(Collection> buildCompatibleExtensions, ClassLoader cl) { this.util = new ExtensionInvoker(buildCompatibleExtensions);