From e1467ea40bcf171cdc6396d16e86f08aaa66ed6f Mon Sep 17 00:00:00 2001 From: jansupol <15908245+jansupol@users.noreply.github.com> Date: Fri, 3 Sep 2021 12:23:14 +0200 Subject: [PATCH] New CDI based EE injection manager incubating implementation. (#4822) * New CDI based EE injection manager incubating implementation. Signed-off-by: jansupol --- incubator/cdi-inject-weld/pom.xml | 148 +++ .../inject/weld/internal/bean/BeanHelper.java | 420 ++++++++ .../inject/weld/internal/bean/ClassBean.java | 140 +++ .../bean/InitializableInstanceBean.java | 80 ++ .../InitializableSupplierInstanceBean.java | 118 +++ ...itializableSupplierInstanceBeanBridge.java | 105 ++ .../InitializableSupplierThreadScopeBean.java | 141 +++ .../inject/weld/internal/bean/JerseyBean.java | 194 ++++ .../internal/bean/SupplierBeanBridge.java | 160 +++ .../weld/internal/bean/SupplierClassBean.java | 133 +++ .../weld/internal/data/BindingBeanPair.java | 47 + .../inject/InitializableInstanceBinding.java | 186 ++++ .../InitializableSupplierInstanceBinding.java | 227 +++++ .../internal/inject/MatchableBinding.java | 135 +++ .../injector/AbstractInjectionTarget.java | 70 ++ .../injector/CachedConstructorAnalyzer.java | 159 +++ .../weld/internal/injector/Collector.java | 88 ++ .../ContextInjectionResolverImpl.java | 151 +++ .../internal/injector/InjectionUtils.java | 311 ++++++ .../JerseyClientCreationalContext.java | 155 +++ .../JerseyConstructorInjectionPoint.java | 176 ++++ .../injector/JerseyInjectionTarget.java | 342 +++++++ .../injector/JerseyInstanceInjector.java | 78 ++ .../injector/JerseyProxyResolver.java | 195 ++++ .../injector/JerseyTwofoldInstantiator.java | 75 ++ .../internal/injector/MultiException.java | 201 ++++ .../weld/internal/injector/NamedImpl.java | 55 ++ .../internal/injector/ReflectionUtils.java | 428 ++++++++ .../WrappingJerseyInjectionTarget.java | 130 +++ .../managed/BinderRegisterExtension.java | 912 ++++++++++++++++++ .../managed/CdiClientInjectionManager.java | 151 +++ .../internal/managed/CdiInjectionManager.java | 370 +++++++ .../CdiInjectionManagerFactoryBase.java | 38 + .../ClientBootstrapPreinitialization.java | 51 + .../ServerBootstrapPreinitialization.java | 649 +++++++++++++ .../managed/WrappingInjectionManager.java | 142 +++ .../internal/scope/CdiRequestContext.java | 103 ++ .../weld/internal/scope/CdiRequestScope.java | 91 ++ .../weld/internal/scope/RequestScopeBean.java | 118 +++ .../internal/type/GenericArrayTypeImpl.java | 94 ++ .../internal/type/ParameterizedTypeImpl.java | 107 ++ .../managed/CdiInjectionManagerFactory.java | 78 ++ .../weld/spi/BootstrapPreinitialization.java | 38 + .../javax.enterprise.inject.spi.Extension | 17 + ...inject.weld.spi.BootstrapPreinitialization | 17 + ...ey.internal.inject.InjectionManagerFactory | 17 + .../internal/managed/localization.properties | 18 + .../CachedConstructorAnalyzerTest.java | 223 +++++ .../injector/JerseyProxyResolverTest.java | 192 ++++ .../internal/managed/BindingTestHelper.java | 58 ++ .../managed/ClientInstanceInjectionTest.java | 226 +++++ .../weld/internal/managed/Conversation.java | 36 + .../internal/managed/CzechConversation.java | 36 + .../weld/internal/managed/CzechGreeting.java | 43 + .../managed/DisposableSupplierTest.java | 595 ++++++++++++ .../internal/managed/EnglishGreeting.java | 43 + .../weld/internal/managed/Greeting.java | 32 + .../managed/InjectionManagerTest.java | 56 ++ .../managed/MyVetoedLongSupplier.java | 33 + .../weld/internal/managed/Printable.java | 27 + .../managed/PrintableConversation.java | 42 + .../managed/ProviderInjectionTest.java | 232 +++++ .../managed/SupplierClassBindingTest.java | 312 ++++++ .../managed/SupplierContractsTest.java | 322 +++++++ .../internal/managed/SupplierGreeting.java | 55 ++ .../managed/SupplierInstanceBindingTest.java | 239 +++++ .../weld/internal/managed/TestParent.java | 48 + .../managed/TestPreinitialization.java | 116 +++ .../internal/managed/ThreadScopeTest.java | 397 ++++++++ .../src/test/resources/META-INF/beans.xml | 20 + ...inject.weld.spi.BootstrapPreinitialization | 17 + incubator/pom.xml | 1 + tests/e2e-inject/cdi-inject-weld/pom.xml | 87 ++ .../tests/e2e/inject/cdi/weld/Account.java | 43 + .../e2e/inject/cdi/weld/AccountResource.java | 61 ++ .../tests/e2e/inject/cdi/weld/Credit.java | 39 + .../tests/e2e/inject/cdi/weld/Debit.java | 39 + .../tests/e2e/inject/cdi/weld/Hello.java | 28 + .../e2e/inject/cdi/weld/HelloResource.java | 46 + .../inject/cdi/weld/HelloStarDecorator.java | 41 + .../e2e/inject/cdi/weld/JaxrsService.java | 38 + .../e2e/inject/cdi/weld/NameService.java | 34 + .../tests/e2e/inject/cdi/weld/Secured.java | 39 + .../inject/cdi/weld/SecurityInterceptor.java | 53 + .../weld/scopes/ApplicationCounterBean.java | 36 + .../weld/scopes/RequestScopedResource.java | 57 ++ .../weld/scopes/SingletonScopedResource.java | 50 + .../subresources/ModelProcessorFeature.java | 130 +++ .../inject/cdi/weld/subresources/MyBean.java | 35 + .../cdi/weld/subresources/RootResource.java | 39 + .../subresources/RootSingletonResource.java | 34 + .../subresources/SubResourceSingleton.java | 32 + .../src/main/resources/META-INF/beans.xml | 31 + .../tests/e2e/inject/cdi/weld/EventsTest.java | 80 ++ .../cdi/weld/InterceptorDecoratorTest.java | 79 ++ .../cdi/weld/RequestContextBuilder.java | 206 ++++ .../inject/cdi/weld/scopes/ScopesTest.java | 77 ++ .../subresources/ModelProcessorScopeTest.java | 140 +++ .../src/test/resources/surefire.policy | 49 + tests/e2e-inject/pom.xml | 1 + .../context-inject-on-server/pom.xml | 65 +- .../cdi/inject/ApplicationInjectParent.java | 10 +- .../inject/ParentContainerRequestFilter.java | 4 +- .../inject/ParentContainerResponseFilter.java | 4 +- .../jersey/tests/cdi/inject/ParentInject.java | 14 +- 105 files changed, 12951 insertions(+), 30 deletions(-) create mode 100644 incubator/cdi-inject-weld/pom.xml create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/ClassBean.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableInstanceBean.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierInstanceBean.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierInstanceBeanBridge.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierThreadScopeBean.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/JerseyBean.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/SupplierBeanBridge.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/SupplierClassBean.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/data/BindingBeanPair.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/inject/InitializableInstanceBinding.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/inject/InitializableSupplierInstanceBinding.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/inject/MatchableBinding.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/AbstractInjectionTarget.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/CachedConstructorAnalyzer.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/Collector.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/ContextInjectionResolverImpl.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/InjectionUtils.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyClientCreationalContext.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyConstructorInjectionPoint.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyInjectionTarget.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyInstanceInjector.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyProxyResolver.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyTwofoldInstantiator.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/MultiException.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/NamedImpl.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/ReflectionUtils.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/WrappingJerseyInjectionTarget.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/CdiClientInjectionManager.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/CdiInjectionManager.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/CdiInjectionManagerFactoryBase.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/ClientBootstrapPreinitialization.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/ServerBootstrapPreinitialization.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/WrappingInjectionManager.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/CdiRequestContext.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/CdiRequestScope.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/RequestScopeBean.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/type/GenericArrayTypeImpl.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/type/ParameterizedTypeImpl.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/managed/CdiInjectionManagerFactory.java create mode 100644 incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/spi/BootstrapPreinitialization.java create mode 100644 incubator/cdi-inject-weld/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension create mode 100644 incubator/cdi-inject-weld/src/main/resources/META-INF/services/org.glassfish.jersey.inject.weld.spi.BootstrapPreinitialization create mode 100644 incubator/cdi-inject-weld/src/main/resources/META-INF/services/org.glassfish.jersey.internal.inject.InjectionManagerFactory create mode 100644 incubator/cdi-inject-weld/src/main/resources/org/glassfish/jersey/inject/weld/internal/managed/localization.properties create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/injector/CachedConstructorAnalyzerTest.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyProxyResolverTest.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/BindingTestHelper.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ClientInstanceInjectionTest.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/Conversation.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechConversation.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechGreeting.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/DisposableSupplierTest.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/EnglishGreeting.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/Greeting.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/InjectionManagerTest.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/MyVetoedLongSupplier.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/Printable.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/PrintableConversation.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ProviderInjectionTest.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierClassBindingTest.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierContractsTest.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierGreeting.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierInstanceBindingTest.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestParent.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestPreinitialization.java create mode 100644 incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ThreadScopeTest.java create mode 100644 incubator/cdi-inject-weld/src/test/resources/META-INF/beans.xml create mode 100644 incubator/cdi-inject-weld/src/test/resources/META-INF/services/org.glassfish.jersey.inject.weld.spi.BootstrapPreinitialization create mode 100644 tests/e2e-inject/cdi-inject-weld/pom.xml create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Account.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/AccountResource.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Credit.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Debit.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Hello.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/HelloResource.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/HelloStarDecorator.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/JaxrsService.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/NameService.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Secured.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/SecurityInterceptor.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/ApplicationCounterBean.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/RequestScopedResource.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/SingletonScopedResource.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/ModelProcessorFeature.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/MyBean.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/RootResource.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/RootSingletonResource.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/SubResourceSingleton.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/main/resources/META-INF/beans.xml create mode 100644 tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/EventsTest.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/InterceptorDecoratorTest.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/RequestContextBuilder.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/ScopesTest.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/ModelProcessorScopeTest.java create mode 100644 tests/e2e-inject/cdi-inject-weld/src/test/resources/surefire.policy diff --git a/incubator/cdi-inject-weld/pom.xml b/incubator/cdi-inject-weld/pom.xml new file mode 100644 index 0000000000..44b60b7738 --- /dev/null +++ b/incubator/cdi-inject-weld/pom.xml @@ -0,0 +1,148 @@ + + + + + 4.0.0 + + + org.glassfish.jersey.incubator + project + 2.35-SNAPSHOT + + + jersey-cdi-inject-weld + jar + jersey-inject-cdi-weld + + CDI InjectionManager implementation + + + + org.glassfish.jersey.core + jersey-common + ${project.version} + + + org.glassfish.jersey.core + jersey-client + ${project.version} + + + org.glassfish.jersey.core + jersey-server + ${project.version} + + + + org.glassfish.jersey.containers + jersey-container-servlet-core + ${project.version} + provided + + + + jakarta.servlet + jakarta.servlet-api + ${servlet4.version} + provided + + + org.glassfish.jersey.containers + jersey-container-grizzly2-http + ${project.version} + provided + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + provided + + + org.jboss.weld.se + weld-se-core + provided + + + + junit + junit + test + + + jakarta.el + jakarta.el-api + test + + + + + + + src/main/resources + true + + + + + + com.sun.istack + istack-commons-maven-plugin + true + + + org.codehaus.mojo + build-helper-maven-plugin + true + + + org.apache.felix + maven-bundle-plugin + true + true + + + + org.glassfish.jersey.inject.weld.managed.*;version=${project.version} + + + sun.misc.*;resolution:=optional, + ${jakarta.annotation.osgi.version}, + * + + + true + + + + org.apache.maven.plugins + maven-jar-plugin + + + default-jar + package + + jar + + + + + + + diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java new file mode 100644 index 0000000000..d1fbafae5d --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/BeanHelper.java @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.bean; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; +import java.util.function.Supplier; + +import javax.enterprise.inject.spi.AfterBeanDiscovery; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.InjectionTarget; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.Path; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.inject.weld.internal.data.BindingBeanPair; +import org.glassfish.jersey.inject.weld.internal.inject.InitializableInstanceBinding; +import org.glassfish.jersey.inject.weld.internal.inject.InitializableSupplierInstanceBinding; +import org.glassfish.jersey.inject.weld.internal.injector.CachedConstructorAnalyzer; +import org.glassfish.jersey.inject.weld.internal.injector.InjectionUtils; +import org.glassfish.jersey.inject.weld.internal.injector.JerseyConstructorInjectionPoint; +import org.glassfish.jersey.inject.weld.internal.injector.JerseyInjectionTarget; +import org.glassfish.jersey.inject.weld.internal.injector.JerseyTwofoldInstantiator; +import org.glassfish.jersey.inject.weld.internal.injector.WrappingJerseyInjectionTarget; +import org.glassfish.jersey.internal.inject.ClassBinding; +import org.glassfish.jersey.internal.inject.InjectionResolver; +import org.glassfish.jersey.internal.inject.PerThread; +import org.glassfish.jersey.internal.inject.SupplierClassBinding; + +import org.jboss.weld.annotated.enhanced.EnhancedAnnotatedConstructor; +import org.jboss.weld.annotated.enhanced.EnhancedAnnotatedType; +import org.jboss.weld.annotated.enhanced.jlr.ConstructorSignatureImpl; +import org.jboss.weld.annotated.enhanced.jlr.EnhancedAnnotatedTypeImpl; +import org.jboss.weld.annotated.slim.SlimAnnotatedType; +import org.jboss.weld.bean.builtin.BeanManagerProxy; +import org.jboss.weld.injection.ConstructorInjectionPoint; +import org.jboss.weld.injection.producer.AbstractInstantiator; +import org.jboss.weld.injection.producer.BasicInjectionTarget; +import org.jboss.weld.injection.producer.BeanInjectionTarget; +import org.jboss.weld.injection.producer.InjectionTargetService; +import org.jboss.weld.injection.producer.Instantiator; +import org.jboss.weld.injection.producer.NonProducibleInjectionTarget; +import org.jboss.weld.manager.BeanManagerImpl; +import org.jboss.weld.resources.ClassTransformer; + +/** + * Helper class to register a {@link Bean} into CDI {@link BeanManager}. + */ +public abstract class BeanHelper { + + /** + * Forbids the creation of {@link BeanHelper} instance. + */ + private BeanHelper() { + } + + /** + * Registers an instance as {@link JerseyBean} into {@link BeanManager}. + * + * @param binding object containing {@link javax.enterprise.inject.spi.BeanAttributes} information. + * @param abd {@link AfterBeanDiscovery} event. + * @param resolvers all registered injection resolvers. + * @param type of the instance which is registered. + */ + public static void registerBean(RuntimeType runtimeType, InitializableInstanceBinding binding, AfterBeanDiscovery abd, + List resolvers, BeanManager beanManager) { + InitializableInstanceBean bean = new InitializableInstanceBean<>(runtimeType, binding); + /* + * Wrap into custom injection target that is able to inject the additional @Inject, @Context, @*Param fields into + * the given service. + */ + InjectionTarget injectionTarget = new WrappingJerseyInjectionTarget<>(bean, resolvers); + bean.setInjectionTarget(injectionTarget); + abd.addBean(bean); + } + + /** + * Registers a class as {@link JerseyBean} into {@link BeanManager}. + * + * @param binding object containing {@link javax.enterprise.inject.spi.BeanAttributes} information. + * @param abd {@link AfterBeanDiscovery} event. + * @param resolvers all registered injection resolvers. + * @param beanManager currently used bean manager. + * @param type of the class which is registered. + */ + public static BindingBeanPair registerBean(RuntimeType runtimeType, ClassBinding binding, AfterBeanDiscovery abd, + Collection resolvers, BeanManager beanManager) { + AnnotatedType annotatedType = beanManager.createAnnotatedType(binding.getService()); + InjectionTarget injectionTarget = beanManager.createInjectionTarget(annotatedType); + + ClassBean bean = new ClassBean<>(runtimeType, binding); + bean.setInjectionTarget(getJerseyInjectionTarget(binding.getService(), injectionTarget, bean, resolvers)); + abd.addBean(bean); + + return new BindingBeanPair(binding, bean); + } + + /** + * Registers an instance supplier and its provided value as {@link JerseyBean}s into {@link BeanManager}. + * + * @param binding object containing {@link javax.enterprise.inject.spi.BeanAttributes} information. + * @param abd {@link AfterBeanDiscovery} event. + * @param type of the instance which is registered. + */ + public static void registerSupplier(RuntimeType runtimeType, InitializableSupplierInstanceBinding binding, + AfterBeanDiscovery abd, BeanManager beanManager) { + /* + * CDI does not provide sufficient support for ThreadScoped Supplier + */ + if (binding.getScope() == PerThread.class) { + BeanManagerImpl manager; + if (beanManager instanceof BeanManagerProxy) { + manager = ((BeanManagerProxy) beanManager).unwrap(); + } else { + manager = (BeanManagerImpl) beanManager; + } + abd.addBean(new InitializableSupplierThreadScopeBean(runtimeType, binding, manager)); + } else { + abd.addBean(new InitializableSupplierInstanceBean<>(runtimeType, binding)); + abd.addBean(new InitializableSupplierInstanceBeanBridge<>(runtimeType, binding)); + } + } + + /** + * Registers a class supplier and its provided value as {@link JerseyBean}s into {@link BeanManager}. + * + * @param binding object containing {@link javax.enterprise.inject.spi.BeanAttributes} information. + * @param abd {@link AfterBeanDiscovery} event. + * @param resolvers all registered injection resolvers. + * @param beanManager currently used bean manager. + * @param type of the class which is registered. + */ + @SuppressWarnings("unchecked") + public static BindingBeanPair registerSupplier(RuntimeType runtimeType, SupplierClassBinding binding, + AfterBeanDiscovery abd, Collection resolvers, BeanManager beanManager) { + + Class> supplierClass = (Class>) binding.getSupplierClass(); + AnnotatedType> annotatedType = beanManager.createAnnotatedType(supplierClass); + InjectionTarget> injectionTarget = beanManager.createInjectionTarget(annotatedType); + + SupplierClassBean supplierBean = new SupplierClassBean<>(runtimeType, binding); + InjectionTarget> jit = getJerseyInjectionTarget(supplierClass, injectionTarget, supplierBean, resolvers); + supplierBean.setInjectionTarget(jit); + + final SupplierBeanBridge supplierBeanBridge = new SupplierBeanBridge(runtimeType, binding, beanManager); + + abd.addBean(supplierBean); + abd.addBean(supplierBeanBridge); + + return new BindingBeanPair(binding, supplierBean, supplierBeanBridge); + } + + /** + * Update ClassBinding Bean by {@link ConstructorInjectionPoint} for the client side beans. + * @param binding The ClassBinding used to create a client side ConstructorInjectionPoint. + * @param pair {@link BindingBeanPair} that contains the original server side Bean. + * @param resolvers Resolvers handling Jersey specific injection annotations. + * @param beanManager The {@link BeanManager}. + */ + public static void updateBean(ClassBinding binding, + BindingBeanPair pair, Collection resolvers, BeanManager beanManager) { + + final JerseyBean bean = pair.getBeans().get(0); + final ConstructorInjectionPoint cip = createConstructorInjectionPoint(binding, bean, resolvers, beanManager); + + if (ClassBean.class.isInstance(bean) + && JerseyInjectionTarget.class.isInstance(((ClassBean) bean).getInjectionTarget())) { + final JerseyTwofoldInstantiator instantiator = + ((JerseyInjectionTarget) ((ClassBean) bean).getInjectionTarget()).getTwofoldInstantiator(); + instantiator.setOptionalConstructorInjectionPoint(cip); + } + } + + /** + * Update SupplierClassBinding Bean by {@link ConstructorInjectionPoint} for the client side beans. + * @param binding The SupplierClassBinding used to create a client side ConstructorInjectionPoint. + * @param pair {@link BindingBeanPair} that contains the original server side Bean. + * @param resolvers Resolvers handling Jersey specific injection annotations. + * @param beanManager The {@link BeanManager}. + */ + public static void updateSupplierBean(SupplierClassBinding binding, + BindingBeanPair pair, Collection resolvers, BeanManager beanManager) { + + final JerseyBean bean = pair.getBeans().get(0); + final ConstructorInjectionPoint cip = createConstructorInjectionPoint(binding, bean, resolvers, beanManager); + + if (SupplierClassBean.class.isInstance(bean) + && JerseyInjectionTarget.class.isInstance(((SupplierClassBean) bean).getInjectionTarget())) { + final JerseyTwofoldInstantiator instantiator = + ((JerseyInjectionTarget) ((SupplierClassBean) bean).getInjectionTarget()).getTwofoldInstantiator(); + instantiator.setOptionalConstructorInjectionPoint(cip); + } + } + + private static ConstructorInjectionPoint createConstructorInjectionPoint( + SupplierClassBinding binding, Bean bean, Collection resolvers, BeanManager beanManager) { + + final Class> bindingClass = (Class>) binding.getSupplierClass(); + final AnnotatedType> annotatedType = beanManager.createAnnotatedType(bindingClass); + final InjectionTarget> injectionTarget = beanManager.createInjectionTarget(annotatedType); + + final CachedConstructorAnalyzer> analyzer = + new CachedConstructorAnalyzer<>(bindingClass, InjectionUtils.getInjectAnnotations(resolvers)); + + if (analyzer.hasCompatibleConstructor()) { + EnhancedAnnotatedConstructor constructor = createEnhancedAnnotatedType((BasicInjectionTarget) injectionTarget) + .getDeclaredEnhancedConstructor(new ConstructorSignatureImpl(analyzer.getConstructor())); + + JerseyConstructorInjectionPoint constructorInjectionPoint = new JerseyConstructorInjectionPoint( + constructor, bean, ((BasicInjectionTarget) injectionTarget).getBeanManager(), resolvers); + return constructorInjectionPoint; + } + return null; + } + + private static ConstructorInjectionPoint createConstructorInjectionPoint( + ClassBinding binding, Bean bean, Collection resolvers, BeanManager beanManager) { + + final Class bindingClass = binding.getImplementationType(); + final AnnotatedType annotatedType = beanManager.createAnnotatedType(bindingClass); + final InjectionTarget injectionTarget = beanManager.createInjectionTarget(annotatedType); + + final CachedConstructorAnalyzer analyzer = + new CachedConstructorAnalyzer<>(bindingClass, InjectionUtils.getInjectAnnotations(resolvers)); + + if (analyzer.hasCompatibleConstructor()) { + EnhancedAnnotatedConstructor constructor = createEnhancedAnnotatedType((BasicInjectionTarget) injectionTarget) + .getDeclaredEnhancedConstructor(new ConstructorSignatureImpl(analyzer.getConstructor())); + + JerseyConstructorInjectionPoint constructorInjectionPoint = new JerseyConstructorInjectionPoint( + constructor, bean, ((BasicInjectionTarget) injectionTarget).getBeanManager(), resolvers); + return constructorInjectionPoint; + } + return null; + } + + private static InjectionTarget getJerseyInjectionTarget(Class clazz, InjectionTarget injectionTarget, + Bean bean, Collection resolvers) { + BasicInjectionTarget it = (BasicInjectionTarget) injectionTarget; + + /* + * Looks at whether the DefaultInstantiator resolving a valid constructor does not met this case: + * - No constructor with @Inject annotation is defined + * - NoArgs constructor is defined + * - Instantiator ignores JAX-RS valid constructor with multiple params + */ + boolean noArgConstructor = isNoArgConstructorCase(it, clazz); + + JerseyInjectionTarget jit; + /* + * CDI is able to find a constructor that means that the class contains only one constructor of this type: + * - default constructor + * - non-argument constructor + * - multi-param constructor annotated by @Inject annotation and able to inject all parameters. + */ + if (!noArgConstructor && injectionTarget instanceof BeanInjectionTarget) { + jit = new JerseyInjectionTarget<>(it, bean, clazz, resolvers); + + /* + * CDI failed during the looking for a proper constructor because of these reasons: + * - multi-param constructor not annotated by @Inject annotation + * - multiple constructors annotated by @Inject annotation + * - is not able to satisfied single constructor annotated by @Inject annotation + * + * Therefore produced NonProducibleInjectionTarget cannot create and instance, we try to find the proper constructor + * using JAX-RS rules: + * - largest constructor with all annotated parameters + * + * If JAX-RS valid constructor is not find - InjectionException is thrown + */ + } else if (noArgConstructor || injectionTarget instanceof NonProducibleInjectionTarget) { + CachedConstructorAnalyzer analyzer = + new CachedConstructorAnalyzer<>(clazz, InjectionUtils.getInjectAnnotations(resolvers)); + + /* + * Contains the analyzed class any constructor that can be injected by Jersey? + */ + if (analyzer.hasCompatibleConstructor()) { + EnhancedAnnotatedConstructor constructor = createEnhancedAnnotatedType(it) + .getDeclaredEnhancedConstructor(new ConstructorSignatureImpl(analyzer.getConstructor())); + + JerseyConstructorInjectionPoint constructorInjectionPoint = + new JerseyConstructorInjectionPoint<>(constructor, bean, it.getBeanManager(), resolvers); + + Instantiator instantiator = new JerseyInstantiator<>(constructorInjectionPoint); + jit = new JerseyInjectionTarget<>(createEnhancedAnnotatedType(it), it, bean, clazz, resolvers, instantiator); + + /* + * Instance of this class cannot be created neither CDI nor Jersey therefore mark it as non-producible. + */ + } else { + return new WrappingJerseyInjectionTarget<>(it, bean, resolvers); + } + } else { + throw new RuntimeException("Unknown InjectionTarget for the class: " + clazz.getTypeName()); + } + + InjectionTargetService injectionTargetService = it.getBeanManager().getServices().get(InjectionTargetService.class); + injectionTargetService.addInjectionTargetToBeInitialized(jit.getEnhancedAnnotatedType(), jit); + return jit; + } + + public static EnhancedAnnotatedType createEnhancedAnnotatedType(BasicInjectionTarget it) { + return EnhancedAnnotatedTypeImpl.of( + (SlimAnnotatedType) it.getAnnotatedType(), ClassTransformer.instance(it.getBeanManager())); + } + + /** + * Looks at whether the DefaultInstantiator resolving a valid constructor does not met this case: + * - No constructor with @Inject annotation is defined + * - NoArgs constructor is defined + * - Instantiator ignores JAX-RS valid constructor with multiple params + * + * @param it injection target containing instantiator with resolved constructor. + * @param clazz class which analyzed constructor belongs to. + * @param type of the analyzed class. + * @return {@code true} if no-arg constructor was selected while multi-params constructor exists. + */ + private static boolean isNoArgConstructorCase(BasicInjectionTarget it, Class clazz) { + if (!(it instanceof NonProducibleInjectionTarget)) { + Instantiator instantiator = it.getInstantiator(); + Constructor constructor = instantiator.getConstructor(); + return constructor.getParameterCount() == 0 && clazz.getConstructors().length > 1; + } + + return false; + } + + /** + * Wrapper class to provide Jersey implementation of {@link Instantiator} interface. + * + * @param type which is created by instantiator. + */ + private static class JerseyInstantiator extends AbstractInstantiator { + + private final ConstructorInjectionPoint injectionPoint; + + private JerseyInstantiator(ConstructorInjectionPoint injectionPoint) { + this.injectionPoint = injectionPoint; + } + + @Override + public ConstructorInjectionPoint getConstructorInjectionPoint() { + return injectionPoint; + } + + @Override + public Constructor getConstructor() { + return injectionPoint.getAnnotated().getJavaMember(); + } + + @Override + public String toString() { + return "JerseyInstantiator [constructor=" + injectionPoint.getMember() + "]"; + } + + @Override + public boolean hasInterceptorSupport() { + return false; + } + + @Override + public boolean hasDecoratorSupport() { + return false; + } + } + + public static boolean isResourceClass(Class clazz) { + if (isJaxrsResource(clazz)) { + return true; + } + + for (Class iface : clazz.getInterfaces()) { + if (isJaxrsResource(iface)) { + return true; + } + } + + return false; + } + + private static boolean isJaxrsResource(Class clazz) { + if (clazz.isAnnotationPresent(Path.class)) { + return true; + } + + for (Method method : clazz.getMethods()) { + if (method.isAnnotationPresent(Path.class)) { + return true; + } + + for (Annotation annotation : method.getAnnotations()) { + if (annotation.annotationType().isAnnotationPresent(HttpMethod.class)) { + return true; + } + } + } + + return false; + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/ClassBean.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/ClassBean.java new file mode 100644 index 0000000000..6c3d6b828d --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/ClassBean.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.bean; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Set; + +import javax.enterprise.context.Dependent; +import javax.enterprise.context.RequestScoped; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.inject.spi.InjectionTarget; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.inject.weld.internal.injector.JerseyInjectionTarget; +import org.glassfish.jersey.internal.inject.ClassBinding; + +/** + * Creates an implementation of {@link javax.enterprise.inject.spi.Bean} interface using Jersey's {@link ClassBinding}. Binding + * provides the information about the bean also called {@link javax.enterprise.inject.spi.BeanAttributes} information and + * {@link JerseyInjectionTarget} provides the contextual part of the bean because implements + * {@link javax.enterprise.context.spi.Contextual} with Jersey injection extension (is able to inject into JAX-RS/Jersey specified + * annotation). + *

+ * Inject example: + *

+ * AbstractBinder {
+ *     @Override
+ *     protected void configure() {
+ *         bind(MyBean.class)
+ *              .to(MyBean.class)
+ *              .in(Singleton.class);
+ *     }
+ * }
+ * 
+ * Register example: + *
+ *  @Path("/")
+ *  public class MyResource {
+ *    @Inject
+ *    private MyBean myBean;
+ *  }
+ * 
+ * + * @author Petr Bouda + */ +class ClassBean extends JerseyBean { + + private final ClassBinding binding; + private InjectionTarget injectionTarget; + + /** + * Creates a new Jersey-specific {@link javax.enterprise.inject.spi.Bean} instance. + * @param runtimeType {@link RuntimeType} type information of the bean source. + * @param binding the binding information. + */ + ClassBean(RuntimeType runtimeType, ClassBinding binding) { + super(runtimeType, binding); + this.binding = binding; + } + + @Override + public Class getScope() { + /* + * Resource class without the Scope annotation should registered as a RequestScoped. + */ + if (binding.getScope() == null && BeanHelper.isResourceClass(binding.getService())) { + return RequestScoped.class; + } + + return binding.getScope() == null ? Dependent.class : transformScope(binding.getScope()); + } + + @Override + @SuppressWarnings("unchecked") + public T create(CreationalContext context) { + T instance = injectionTarget.produce(context); + injectionTarget.inject(instance, context); + injectionTarget.postConstruct(instance); + return instance; + } + + @Override + public void destroy(T instance, CreationalContext context) { + injectionTarget.preDestroy(instance); + injectionTarget.dispose(instance); + context.release(); + } + + @Override + public Set getTypes() { + Set contracts = super.getTypes(); + contracts.addAll(Arrays.asList(binding.getService().getInterfaces())); + return contracts; + } + + @Override + public Class getBeanClass() { + return binding.getService(); + } + + @Override + public Set getInjectionPoints() { + return injectionTarget.getInjectionPoints(); + } + + /** + * Lazy set of an injection target because to create fully functional injection target needs already created bean. + * + * @param injectionTarget {@link javax.enterprise.context.spi.Contextual} information belonging to this bean. + */ + void setInjectionTarget(InjectionTarget injectionTarget) { + this.injectionTarget = injectionTarget; + } + + @Override + public String toString() { + return "ClassBean{" + getBeanClass() + "(" + getRutimeType() + ")}"; + } + + public InjectionTarget getInjectionTarget() { + return injectionTarget; + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableInstanceBean.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableInstanceBean.java new file mode 100644 index 0000000000..f319a4f34d --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableInstanceBean.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ +package org.glassfish.jersey.inject.weld.internal.bean; + +import javax.enterprise.context.Dependent; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.InjectionTarget; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.inject.weld.internal.inject.InitializableInstanceBinding; +import org.glassfish.jersey.inject.weld.internal.injector.JerseyClientCreationalContext; + +import java.lang.annotation.Annotation; + +/** + * Instance bean to be created in the pre-initialization phase and initialized after Jersey is bootstrap. + * @param the class of the bean instance. + */ +public class InitializableInstanceBean extends JerseyBean { + + private InjectionTarget injectionTarget; + + /** + * Creates a new Jersey-specific {@link javax.enterprise.inject.spi.Bean} instance. + * + * @param binding {@link javax.enterprise.inject.spi.BeanAttributes} part of the bean. + */ + InitializableInstanceBean(RuntimeType runtimeType, InitializableInstanceBinding binding) { + super(runtimeType, binding); + } + + @Override + public Class getScope() { + return getBinding().getScope() == null ? Dependent.class : transformScope(getBinding().getScope()); + } + + @Override + public T create(CreationalContext context) { + InitializableInstanceBinding realBinding = (InitializableInstanceBinding) getBinding(); + if (JerseyClientCreationalContext.class.isInstance(context)) { + realBinding = ((JerseyClientCreationalContext) context).getInjectionManager().getInjectionManagerBinding(realBinding); + } + T service = realBinding.getService(); + this.injectionTarget.inject(service, context); + return service; + } + + @Override + public Class getBeanClass() { + final InitializableInstanceBinding binding = (InitializableInstanceBinding) getBinding(); + return binding.isInit() ? binding.getImplementationType() : Object.class; + } + + /** + * Lazy set of an injection target because to create fully functional injection target needs already created bean. + * + * @param injectionTarget {@link javax.enterprise.context.spi.Contextual} information belonging to this bean. + */ + void setInjectionTarget(InjectionTarget injectionTarget) { + this.injectionTarget = injectionTarget; + } + + @Override + public String toString() { + return "InitializableInstanceBean{" + getBeanClass() + "}"; + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierInstanceBean.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierInstanceBean.java new file mode 100644 index 0000000000..cbaaf0cd94 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierInstanceBean.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.bean; + +import javax.enterprise.context.Dependent; +import javax.enterprise.context.spi.CreationalContext; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.inject.weld.internal.inject.InitializableSupplierInstanceBinding; +import org.glassfish.jersey.inject.weld.internal.injector.JerseyClientCreationalContext; +import org.glassfish.jersey.inject.weld.internal.injector.JerseyInjectionTarget; +import org.glassfish.jersey.inject.weld.internal.type.ParameterizedTypeImpl; +import org.glassfish.jersey.internal.inject.DisposableSupplier; +import org.glassfish.jersey.internal.inject.SupplierInstanceBinding; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; + +/** + * Creates an implementation of {@link javax.enterprise.inject.spi.Bean} interface using Jersey's {@link SupplierInstanceBinding}. + * Binding provides the information about the bean also called {@link javax.enterprise.inject.spi.BeanAttributes} information. + * The {@code Bean} does not use {@link JerseyInjectionTarget} because serves already + * created supplier instance, therefore the create operation just return provided instance without any other contextual operation + * (produce, inject, destroy). Client has to manage the instance alone. + *

+ * Inject example: + *

+ * AbstractBinder {
+ *     @Override
+ *     protected void configure() {
+ *         bindFactory(new MyBeanFactory())
+ *              .to(MyBean.class)
+ *              .in(Singleton.class);
+ *     }
+ * }
+ * 
+ * Register example: + *
+ *  @Path("/")
+ *  public class MyResource {
+ *    @Inject
+ *    private Supplier<MyBean> myBean;
+ *  }
+ * 
+ * + * @author Petr Bouda + */ +class InitializableSupplierInstanceBean extends JerseyBean> { + + private final Set contracts = new HashSet<>(); + private final Supplier supplier; + + /** + * Creates a new Jersey-specific {@link javax.enterprise.inject.spi.Bean} instance. + * + * @param binding {@link javax.enterprise.inject.spi.BeanAttributes} part of the bean. + */ + InitializableSupplierInstanceBean(RuntimeType runtimeType, InitializableSupplierInstanceBinding binding) { + super(runtimeType, binding); + this.supplier = binding.getSupplier(); + + for (Type contract: binding.getContracts()) { + this.contracts.add(new ParameterizedTypeImpl(Supplier.class, contract)); + if (DisposableSupplier.class.isAssignableFrom(binding.getSupplier().getClass())) { + this.contracts.add(new ParameterizedTypeImpl(DisposableSupplier.class, contract)); + } + } + } + + @Override + public Set getTypes() { + return contracts; + } + + @Override + public Set getQualifiers() { + return DEFAULT_QUALIFIERS; + } + + @Override + public Supplier create(CreationalContext> context) { + if (JerseyClientCreationalContext.class.isInstance(context)) { + final InitializableSupplierInstanceBinding binding = (InitializableSupplierInstanceBinding) getBinding(); + final JerseyClientCreationalContext jerseyContext = (JerseyClientCreationalContext) context; + return jerseyContext.getInjectionManager().getInjectionManagerBinding(binding).getSupplier(); + } else { + return supplier; + } + } + + @Override + public Class getBeanClass() { + final InitializableSupplierInstanceBinding binding = (InitializableSupplierInstanceBinding) getBinding(); + return binding.isInit() ? binding.getOriginalSupplier().getClass() : Object.class; + } + + @Override + public Class getScope() { + return getBinding().getScope() == null ? Dependent.class : transformScope(getBinding().getScope()); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierInstanceBeanBridge.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierInstanceBeanBridge.java new file mode 100644 index 0000000000..c86d44f5c1 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierInstanceBeanBridge.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.bean; + +import javax.enterprise.context.Dependent; +import javax.enterprise.context.spi.CreationalContext; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.inject.weld.internal.inject.InitializableSupplierInstanceBinding; +import org.glassfish.jersey.inject.weld.internal.injector.JerseyInjectionTarget; +import org.glassfish.jersey.internal.inject.DisposableSupplier; +import org.glassfish.jersey.internal.inject.SupplierInstanceBinding; + +import java.lang.annotation.Annotation; +import java.util.function.Supplier; + +/** + * Creates an implementation of {@link javax.enterprise.inject.spi.Bean} interface using Jersey's {@link SupplierInstanceBinding}. + * Binding provides the information about the bean also called {@link javax.enterprise.inject.spi.BeanAttributes} information. + * The {@code Bean} does not use {@link JerseyInjectionTarget} because serves already + * created instances, therefore the create operation just return provided instance without any other contextual operation + * (produce, inject, destroy). Client has to manage the instance alone. + *

+ * This implementation works as bridge between {@link Supplier} and its provided value. This solves the case when the concrete + * type of supplier value is fetched from {@link org.glassfish.jersey.internal.inject.InjectionManager} then this + * {@link javax.enterprise.inject.spi.Bean} implementation just invokes {@link Supplier#get} method on underlying/registered + * supplier. + *

+ * Inject example: + *

+ * AbstractBinder {
+ *     @Override
+ *     protected void configure() {
+ *         bindFactory(new MyBeanFactory())
+ *              .to(MyBean.class)
+ *              .in(Singleton.class);
+ *     }
+ * }
+ * 
+ * Register example: + *
+ *  @Path("/")
+ *  public class MyResource {
+ *    @Inject
+ *    private MyBean myBean;
+ *  }
+ * 
+ * + * @author Petr Bouda + */ +class InitializableSupplierInstanceBeanBridge extends JerseyBean { + + private final Supplier supplier; + private final Class scope; + + /** + * Creates a new Jersey-specific {@link javax.enterprise.inject.spi.Bean} instance. + * + * @param binding {@link javax.enterprise.inject.spi.BeanAttributes} part of the bean. + */ + @SuppressWarnings("unchecked") + InitializableSupplierInstanceBeanBridge(RuntimeType runtimeType, InitializableSupplierInstanceBinding binding) { + super(runtimeType, binding); + + InitializableSupplierInstanceBinding casted = (InitializableSupplierInstanceBinding) binding; + this.supplier = casted.getSupplier(); + this.scope = casted.getScope(); + } + + @Override + public Object create(CreationalContext creationalContext) { + return supplier.get(); + } + + @Override + public void destroy(Object instance, CreationalContext context) { + if (DisposableSupplier.class.isAssignableFrom(supplier.getClass())) { + ((DisposableSupplier) supplier).dispose(instance); + } + } + + /** + * {@link InitializableSupplierInstanceBeanBridge} needs have the same scope as a keeping value. + * + * @return scope of the supplier bean. + */ + @Override + public Class getScope() { + return scope == null ? Dependent.class : transformScope(scope); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierThreadScopeBean.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierThreadScopeBean.java new file mode 100644 index 0000000000..5f144e0426 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/InitializableSupplierThreadScopeBean.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.bean; + +import javax.enterprise.context.Dependent; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.PassivationCapable; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.inject.weld.internal.inject.InitializableSupplierInstanceBinding; +import org.glassfish.jersey.inject.weld.internal.injector.JerseyInjectionTarget; +import org.glassfish.jersey.internal.inject.SupplierInstanceBinding; +import org.jboss.weld.bean.StringBeanIdentifier; +import org.jboss.weld.bean.proxy.BeanInstance; +import org.jboss.weld.bean.proxy.ContextBeanInstance; +import org.jboss.weld.bean.proxy.ProxyFactory; +import org.jboss.weld.manager.BeanManagerImpl; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.WeakHashMap; +import java.util.function.Supplier; + +/** + * Creates an implementation of {@link Bean} interface using Jersey's {@link SupplierInstanceBinding}. + * Binding provides the information about the bean also called {@link javax.enterprise.inject.spi.BeanAttributes} information. + * The {@code Bean} does not use {@link JerseyInjectionTarget} because serves already + * created proxy, therefore the create operation just return provided instance without any other contextual operation + * (produce, inject, destroy). + *

+ * This bean is special and is used only for service registered as a {@link org.glassfish.jersey.internal.inject.PerThread} and + * works through the proxy which serves the correct instance per the given thread. + *

+ * Register example: + *

+ * AbstractBinder {
+ *     @Override
+ *     protected void configure() {
+ *         bindFactory(new MyFactoryInjectionProvider())
+ *              .to(MyBean.class)
+ *              .in(PerThread.class);
+ *     }
+ * }
+ * 
+ * Inject example: + *
+ * @Path("/")
+ * public class MyResource {
+ *   @Inject
+ *   private MyBean myBean;
+ * }
+ * 
+ */ +public class InitializableSupplierThreadScopeBean extends JerseyBean { + + private final ThreadScopeBeanInstance beanInstance; + private final InitializableSupplierInstanceBinding binding; + private final Object proxy; + + /** + * Creates a new Jersey-specific {@link Bean} instance. + * + * @param binding {@link javax.enterprise.inject.spi.BeanAttributes} part of the bean. + */ + @SuppressWarnings("unchecked") + InitializableSupplierThreadScopeBean(RuntimeType runtimeType, + InitializableSupplierInstanceBinding binding, + BeanManagerImpl manager) { + super(runtimeType, binding); + this.binding = binding; + this.beanInstance = new ThreadScopeBeanInstance<>(binding.getSupplier(), this, manager.getContextId()); + this.proxy = createClientProxy(beanInstance, manager.getContextId()); + } + + @Override + public Class getScope() { + return Dependent.class; + } + + @Override + public Object create(CreationalContext ctx) { + return proxy; + } + + @Override + public void destroy(Object instance, CreationalContext creationalContext) { + this.beanInstance.dispose(); + } + + @Override + public Class getBeanClass() { + return (Class) this.binding.getContracts().iterator().next(); + } + + private T createClientProxy(BeanInstance beanInstance, String contextId) { + ProxyFactory factory = new ProxyFactory<>(contextId, getBeanClass(), getTypes(), this); + return factory.create(beanInstance); + } + + private static class ThreadScopeBeanInstance extends ContextBeanInstance { + + private final WeakHashMap instances = new WeakHashMap<>(); + + private final Supplier supplier; + + /** + * Creates a new invocation handler with supplier which provides a current injected value in proper scope. + * + * @param supplier provider of the value. + */ + private ThreadScopeBeanInstance(Supplier supplier, Bean bean, String contextId) { + super(bean, new StringBeanIdentifier(((PassivationCapable) bean).getId()), contextId); + this.supplier = supplier; + } + + @Override + public Object invoke(Object obj, Method method, Object... arguments) throws Throwable { + Object instance = instances.computeIfAbsent(Thread.currentThread(), thread -> supplier.get()); + return super.invoke(instance, method, arguments); + } + + public void dispose() { + this.instances.clear(); + } + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/JerseyBean.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/JerseyBean.java new file mode 100644 index 0000000000..99cee8bb87 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/JerseyBean.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.bean; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import javax.annotation.Priority; +import javax.enterprise.context.Dependent; +import javax.enterprise.context.RequestScoped; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.Any; +import javax.enterprise.inject.Default; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.inject.spi.PassivationCapable; +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Singleton; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.internal.inject.Binding; +import org.glassfish.jersey.internal.inject.PerLookup; +import org.glassfish.jersey.internal.inject.PerThread; + +import org.jboss.weld.environment.se.contexts.ThreadScoped; + +/** + * Jersey-specific abstract class which implements {@link Bean} interface. Class particularly contains default implementations + * of {@link Bean} interface. + * + * @author Petr Bouda + */ +public abstract class JerseyBean implements Bean, PassivationCapable { + + static final Set DEFAULT_QUALIFIERS; + + static { + DEFAULT_QUALIFIERS = new HashSet<>(); + DEFAULT_QUALIFIERS.add(new AnnotationLiteral() {}); + DEFAULT_QUALIFIERS.add(new AnnotationLiteral() {}); + } + + public Binding getBinding() { + return binding; + } + + private final Binding binding; + private final RuntimeType runtimeType; + + /** + * JerseyBean constructor with {@link Binding} which represents {@link javax.enterprise.context.spi.Contextual} part of the + * bean. + * + * @param runtimeType + * @param binding information about the bean. + */ + JerseyBean(RuntimeType runtimeType, Binding binding) { + this.binding = binding; + this.runtimeType = runtimeType == null ? RuntimeType.SERVER : runtimeType; + } + + /** + * Transforms Jersey scopes/annotations to HK2 equivalents. + * + * @param scope Jersey scope/annotation. + * @return HK2 equivalent scope/annotation. + */ + protected static Class transformScope(Class scope) { + if (scope == PerLookup.class) { + return Dependent.class; + } else if (scope == PerThread.class) { + return ThreadScoped.class; + } else if (scope == org.glassfish.jersey.process.internal.RequestScoped.class) { + return RequestScoped.class; + } + return scope; + } + + @Override + public Set getTypes() { + Set contracts = new HashSet<>(); + contracts.addAll(binding.getContracts()); + + // Merge aliases with the main bean + if (!binding.getAliases().isEmpty()) { + binding.getAliases().forEach(alias -> contracts.add(alias.getContract())); + } + contracts.add(Object.class); + return contracts; + } + + @Override + public Set getQualifiers() { + Set qualifiers = new HashSet<>(); + qualifiers.addAll(DEFAULT_QUALIFIERS); + if (binding.getQualifiers() != null) { + qualifiers.addAll(binding.getQualifiers()); + } + + // Merge aliases with the main bean + if (!binding.getAliases().isEmpty()) { + binding.getAliases().forEach(alias -> qualifiers.addAll(alias.getQualifiers())); + } + return qualifiers; + } + + @Override + public String getName() { + return binding.getName(); + } + + @Override + public Class getScope() { + return Singleton.class; + } + + @Override + public Set> getStereotypes() { + return Collections.emptySet(); + } + + @Override + public boolean isAlternative() { + return false; + } + + @Override + public boolean isNullable() { + return false; + } + + @Override + public void destroy(T instance, CreationalContext creationalContext) { + } + + @Override + public Set getInjectionPoints() { + return Collections.emptySet(); + } + + public int getRank() { + if (binding.getRank() != null) { + return binding.getRank(); + } + + Class type = binding.getImplementationType(); + if (type != null) { + Priority priority = type.getAnnotation(Priority.class); + if (priority != null) { + return priority.value(); + } + } + + return 1; + } + + @Override + public Class getBeanClass() { + return Object.class; + } + + @Override + public String getId() { + // Object for Lambda + return (getBeanClass().equals(Object.class) ? getContractsAsString() : getBeanClass().getTypeName()) + + "#jersey" + runtimeType; + } + + public RuntimeType getRutimeType() { + return runtimeType; + } + + public String getContractsAsString() { + return Arrays.toString(binding.getContracts().toArray()); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/SupplierBeanBridge.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/SupplierBeanBridge.java new file mode 100644 index 0000000000..0a3c24785b --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/SupplierBeanBridge.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.bean; + +import java.lang.annotation.Annotation; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +import javax.enterprise.context.Dependent; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.inject.weld.internal.injector.JerseyInjectionTarget; +import org.glassfish.jersey.inject.weld.managed.CdiInjectionManagerFactory; +import org.glassfish.jersey.inject.weld.internal.type.ParameterizedTypeImpl; +import org.glassfish.jersey.internal.inject.DisposableSupplier; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.SupplierClassBinding; +import org.glassfish.jersey.internal.inject.SupplierInstanceBinding; + +/** + * Creates an implementation of {@link javax.enterprise.inject.spi.Bean} interface using Jersey's {@link SupplierInstanceBinding}. + * Binding provides the information about the bean also called {@link javax.enterprise.inject.spi.BeanAttributes} information. + * The {@code Bean} does not use {@link JerseyInjectionTarget} because serves already + * the instances created by underlying {@link Supplier} injected target on which the call is delegated. + *

+ * This implementation works as bridge between {@link Supplier} and its provided value. This solves the case when the concrete + * type of supplier value is fetched from {@link org.glassfish.jersey.internal.inject.InjectionManager} then this + * {@link javax.enterprise.inject.spi.Bean} implementation just invokes {@link Supplier#get} method on underlying/registered + * supplier. + *

+ * Inject example: + *

+ * AbstractBinder {
+ *     @Override
+ *     protected void configure() {
+ *         bindFactory(MyBeanFactory.class)
+ *              .to(MyBean.class)
+ *              .in(Singleton.class);
+ *     }
+ * }
+ * 
+ * Register example: + *
+ *  @Path("/")
+ *  public class MyResource {
+ *    @Inject
+ *    private MyBean myBean;
+ *  }
+ * 
+ * + * @author Petr Bouda + */ +class SupplierBeanBridge extends JerseyBean { + + private final BeanManager beanManager; + private ParameterizedType type; + private boolean disposable; + private SupplierClassBinding binding; + + // This bridge can create multiple instances using the method 'provide' therefore must map created suppliers because of + // 'dispose' invocation later on. + // TODO: Key as a WeakReference - prevent objects in scope which never dispose the objects such as PerLookup. + private final Map> disposableSuppliers = new IdentityHashMap<>(); + + /** + * Creates a new Jersey-specific {@link javax.enterprise.inject.spi.Bean} instance. + * + * @param binding {@link javax.enterprise.inject.spi.BeanAttributes} part of the bean. + */ + @SuppressWarnings("unchecked") + SupplierBeanBridge(RuntimeType runtimeType, SupplierClassBinding binding, BeanManager beanManager) { + super(runtimeType, binding); + + // Register wrapper for factory functionality, wrapper automatically call service locator which is able to retrieve + // the service in the proper context and scope. Bridge is registered for all contracts but is able to lookup from + // service locator only using the first contract. + Type contract = null; + if (binding.getContracts().iterator().hasNext()) { + contract = (Type) binding.getContracts().iterator().next(); + } + + this.binding = binding; + this.beanManager = beanManager; + this.disposable = DisposableSupplier.class.isAssignableFrom(binding.getSupplierClass()); + this.type = new ParameterizedTypeImpl(Supplier.class, contract); + } + + @Override + @SuppressWarnings("unchecked") + public Object create(CreationalContext creationalContext) { + if (type != null) { + InjectionManager injectionManager = CdiInjectionManagerFactory.getInjectionManager(creationalContext); + Supplier supplier = injectionManager.getInstance(type); + + //Supplier supplier = getSupplier(beanManager, type); + Object instance = supplier.get(); + if (disposable) { + disposableSuppliers.put(instance, (DisposableSupplier) supplier); + } + return instance; + } else { + return null; + } + } + + @Override + @SuppressWarnings("unchecked") + public void destroy(Object instance, CreationalContext context) { + if (disposable) { + DisposableSupplier disposableSupplier = disposableSuppliers.get(instance); + disposableSupplier.dispose(instance); + disposableSuppliers.remove(instance); + } + } + + private static Supplier getSupplier(BeanManager beanManager, ParameterizedType supplierType) { + Set> beans = beanManager.getBeans(supplierType); + if (beans.isEmpty()) { + return null; + } + + Bean bean = beans.iterator().next(); + CreationalContext ctx = beanManager.createCreationalContext(bean); + return (Supplier) beanManager.getReference(bean, supplierType, ctx); + } + + @Override + @SuppressWarnings("unchecked") + public Class getScope() { + return binding.getScope() == null ? Dependent.class : transformScope(binding.getScope()); + } + + @Override + public Class getBeanClass() { + return this.binding.getContracts().isEmpty() + ? super.getBeanClass() + : (Class) this.binding.getContracts().iterator().next(); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/SupplierClassBean.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/SupplierClassBean.java new file mode 100644 index 0000000000..5ae244d888 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/bean/SupplierClassBean.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.bean; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; + +import javax.enterprise.context.Dependent; +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.InjectionTarget; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.inject.weld.internal.injector.JerseyInjectionTarget; +import org.glassfish.jersey.inject.weld.internal.type.ParameterizedTypeImpl; +import org.glassfish.jersey.internal.inject.DisposableSupplier; +import org.glassfish.jersey.internal.inject.SupplierClassBinding; + +/** + * Creates an implementation of {@link javax.enterprise.inject.spi.Bean} interface using Jersey's {@link SupplierClassBinding}. + * Binding provides the information about the bean also called {@link javax.enterprise.inject.spi.BeanAttributes} information and + * {@link JerseyInjectionTarget} provides the contextual part of the bean because implements + * {@link javax.enterprise.context.spi.Contextual} with Jersey injection extension (is able to inject into JAX-RS/Jersey specified + * annotation). + *

+ * Bean's implementation provides possibility to register {@link Supplier} and {@link DisposableSupplier}. + *

+ * Inject example: + *

+ * AbstractBinder {
+ *     @Override
+ *     protected void configure() {
+ *         bindFactory(MyBeanSupplier.class)
+ *              .to(MyBean.class)
+ *              .in(Singleton.class);
+ *     }
+ * }
+ * 
+ * Register example: + *
+ *  @Path("/")
+ *  public class MyResource {
+ *    @Inject
+ *    private Supplier<MyBean> myBean;
+ *  }
+ * 
+ * + * @author Petr Bouda + */ +class SupplierClassBean extends JerseyBean> { + + private final Set contracts = new HashSet<>(); + private final Class> supplierClass; + private final Class supplierScope; + private InjectionTarget> injectionTarget; + + /** + * Creates a new Jersey-specific {@link javax.enterprise.inject.spi.Bean} instance. + * + * @param binding {@link javax.enterprise.inject.spi.BeanAttributes} part of the bean. + */ + SupplierClassBean(RuntimeType runtimeType, SupplierClassBinding binding) { + super(runtimeType, binding); + this.supplierClass = binding.getSupplierClass(); + this.supplierScope = binding.getSupplierScope(); + + for (Type contract : binding.getContracts()) { + this.contracts.add(new ParameterizedTypeImpl(Supplier.class, contract)); + if (DisposableSupplier.class.isAssignableFrom(supplierClass)) { + this.contracts.add(new ParameterizedTypeImpl(DisposableSupplier.class, contract)); + } + } + } + + @Override + public Class getScope() { + return supplierScope == null ? Dependent.class : transformScope(supplierScope); + } + + @Override + public Set getTypes() { + return contracts; + } + + @Override + public Supplier create(CreationalContext> context) { + Supplier instance = injectionTarget.produce(context); + injectionTarget.inject(instance, context); + injectionTarget.postConstruct(instance); + return instance; + } + + @Override + public void destroy(Supplier instance, CreationalContext> context) { + injectionTarget.preDestroy(instance); + injectionTarget.dispose(instance); + context.release(); + } + + @Override + public Class getBeanClass() { + return supplierClass; + } + + /** + * Lazy set of an injection target because to create fully functional injection target needs already created bean. + * + * @param injectionTarget {@link javax.enterprise.context.spi.Contextual} information belonging to this bean. + */ + void setInjectionTarget(InjectionTarget> injectionTarget) { + this.injectionTarget = injectionTarget; + } + + public InjectionTarget> getInjectionTarget() { + return injectionTarget; + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/data/BindingBeanPair.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/data/BindingBeanPair.java new file mode 100644 index 0000000000..1d0b2c9282 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/data/BindingBeanPair.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ +package org.glassfish.jersey.inject.weld.internal.data; + +import org.glassfish.jersey.inject.weld.internal.bean.JerseyBean; +import org.glassfish.jersey.internal.inject.Binding; + +import java.util.LinkedList; +import java.util.List; + +/** + * Pair of a binding and corresponding Jersey beans. + */ +public class BindingBeanPair { + private final Binding binding; + private final List beans = new LinkedList(); + + public BindingBeanPair(Binding binding, JerseyBean... beans) { + this.binding = binding; + if (beans != null) { + for (JerseyBean bean : beans) { + this.beans.add(bean); + } + } + } + + public Binding getBinding() { + return binding; + } + + public List getBeans() { + return beans; + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/inject/InitializableInstanceBinding.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/inject/InitializableInstanceBinding.java new file mode 100644 index 0000000000..61b4a2c6ec --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/inject/InitializableInstanceBinding.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.inject; + +import org.glassfish.jersey.internal.inject.AliasBinding; +import org.glassfish.jersey.internal.inject.Binding; +import org.glassfish.jersey.internal.inject.Bindings; +import org.glassfish.jersey.internal.inject.InstanceBinding; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Injection binding description of a bean bound directly as a specific instance to be created in a pre-initialization phase + * and initialized in runtime. + * + * @param Type of the service described by this injection binding. + */ +public class InitializableInstanceBinding extends MatchableBinding> implements Cloneable { + + protected T service; + private AtomicBoolean isInit = new AtomicBoolean(false); + private Class implementationType; + + /** + * Creates a service as an instance. + * + * @param service service's instance. + */ + protected InitializableInstanceBinding(T service) { + this(service, null); + } + + /** + * Creates a service as an instance. + * + * @param service service's instance. + * @param contractType service's contractType. + */ + private InitializableInstanceBinding(T service, Type contractType) { + this.service = service; + this.implementationType = service == null ? null : (Class) service.getClass(); + if (contractType != null) { + this.to(contractType); + } + } + + /** + * Gets service' class. + * + * @return service's class. + */ + public T getService() { + if (!isInit.get()) { + String types = Arrays.toString(getContracts().toArray()); + throw new IllegalStateException("Not initialized " + service + "(" + types + ")"); + } + return service; + } + + /** + * Gets service's type. + * + * @return service's type. + */ + public Class getImplementationType() { + return implementationType; + } + + public void init(T service) { + if (!isInit.getAndSet(true)) { + this.service = service; + implementationType = (Class) service.getClass(); + } else if (this.service != service) { + throw new IllegalStateException("Multiple initialized for " + service.getClass()); + } + } + + public boolean isInit() { + return isInit.get(); + } + + public Matching> matches(Binding other) { + return super.matches(other); + } + + @Override + public InitializableInstanceBinding clone() { + throw new RuntimeException(new CloneNotSupportedException()); + } + + public static InitializableInstanceBinding from(InstanceBinding instanceBinding) { + return new InitializableWrappingInstanceBinding(instanceBinding); + } + + @Override + protected MatchLevel bestMatchLevel() { + return MatchLevel.IMPLEMENTATION; + } + + private static class InitializableWrappingInstanceBinding extends InitializableInstanceBinding { + private final InstanceBinding wrapped; + public InitializableWrappingInstanceBinding(InstanceBinding binding) { + super(binding.getService()); + wrapped = binding; + } + + private InitializableWrappingInstanceBinding(InitializableWrappingInstanceBinding binding) { + super(binding.service); + wrapped = binding.wrapped; + } + + @Override + public Class getImplementationType() { + return super.getImplementationType(); + } + + @Override + public T getService() { + return super.getService(); + } + + @Override + public Class getScope() { + return wrapped.getScope(); + } + + @Override + public Set getContracts() { + return wrapped.getContracts(); + } + + @Override + public Integer getRank() { + return wrapped.getRank(); + } + + @Override + public Set getAliases() { + return wrapped.getAliases(); + } + + @Override + public Set getQualifiers() { + return wrapped.getQualifiers(); + } + + @Override + public String getAnalyzer() { + return wrapped.getAnalyzer(); + } + + @Override + public String getName() { + return wrapped.getName(); + } + + @Override + public String toString() { + return "InitializableWrappingInstanceBinding(" + wrapped.getService().getClass() + ")"; + } + + @Override + public InitializableWrappingInstanceBinding clone() { + return new InitializableWrappingInstanceBinding(this); + } + } +} \ No newline at end of file diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/inject/InitializableSupplierInstanceBinding.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/inject/InitializableSupplierInstanceBinding.java new file mode 100644 index 0000000000..d99bf597f8 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/inject/InitializableSupplierInstanceBinding.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.inject; + +import org.glassfish.jersey.internal.inject.AliasBinding; +import org.glassfish.jersey.internal.inject.Binding; +import org.glassfish.jersey.internal.inject.DisposableSupplier; +import org.glassfish.jersey.internal.inject.SupplierInstanceBinding; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; + +/** + * Supplier instance binding to be created in the pre-initialization phase and initialized in runtime. + * @param Type of the supplied service described by this injection binding. + */ +public class InitializableSupplierInstanceBinding + extends MatchableBinding, InitializableSupplierInstanceBinding> + implements Cloneable { + + private final InitializableSupplier supplier; + + /** + * Creates a supplier as an instance. + * + * @param supplier service's instance. + */ + public InitializableSupplierInstanceBinding(Supplier supplier) { + this.supplier = DisposableSupplier.class.isInstance(supplier) + ? new InitializableDisposableSupplier((DisposableSupplier) supplier) + : new InitializableSupplier(supplier); + if ("EmptyReferenceFactory".equals(supplier.getClass().getSimpleName())) { + this.supplier.init(supplier); + this.supplier.isReferencingFactory = true; + } + if ("InitializedReferenceFactory".equals(supplier.getClass().getSimpleName())) { + this.supplier.init(supplier); + this.supplier.isReferencingFactory = true; + } + T t = supplier.get(); + } + + public void init(Supplier supplier) { + this.supplier.init(supplier); + } + + public boolean isInit() { + return supplier.init.get(); + } + + /** + * Gets supplier's instance. + * + * @return supplier's instance. + */ + public Supplier getSupplier() { + return supplier; + } + + public Supplier getOriginalSupplier() { + if (supplier.originalSupplier == null) { + throw new IllegalStateException("Supplier must not be null"); + } + return supplier.originalSupplier; + } + + public static InitializableSupplierInstanceBinding from(SupplierInstanceBinding binding) { + return new InitializableSupplierWrappingInstanceBinding(binding); + } + + @Override + public InitializableSupplierInstanceBinding clone() { + throw new RuntimeException(new CloneNotSupportedException()); + } + + public Matching matches(SupplierInstanceBinding other) { + return matches(this.getOriginalSupplier().getClass(), other.getSupplier().getClass(), other); + } + + public Matching matches(InitializableSupplierInstanceBinding other) { + return matches(this.getOriginalSupplier().getClass(), other.getSupplier().getClass(), other); + } + + private Matching> matches( + Class originalSupplierClass, Class otherSupplierClass, Binding other) { + final boolean matchesService = originalSupplierClass.equals(otherSupplierClass); + final Matching matching = matchesContracts(other); + if (matching.matchLevel == MatchLevel.FULL_CONTRACT && matchesService) { + matching.matchLevel = MatchLevel.SUPPLIER; + } + return matching; + } + + @Override + protected MatchLevel bestMatchLevel() { + return MatchLevel.SUPPLIER; + } + + private static class InitializableDisposableSupplier extends InitializableSupplier implements DisposableSupplier { + private InitializableDisposableSupplier(DisposableSupplier originalSupplier) { + super(originalSupplier); + } + + @Override + public void dispose(T instance) { + ((DisposableSupplier) supplier).dispose(instance); + } + } + + private static class InitializableSupplier implements Supplier { + + private AtomicBoolean init = new AtomicBoolean(false); + protected Supplier supplier; + protected final Supplier originalSupplier; + private boolean isReferencingFactory = false; + + private InitializableSupplier(Supplier originalSupplier) { + this.originalSupplier = originalSupplier; + } + + private void init(Supplier supply) { + if (!init.getAndSet(true)) { + this.supplier = supply; + } else if (!isReferencingFactory && supplier != supply) { + throw new IllegalStateException("Multiple initialized for " + originalSupplier.getClass()); + } + } + + @Override + public T get() { + if (!init.get()) { + throw new IllegalStateException("Not initialized" + originalSupplier.getClass()); + } + return supplier.get(); + } + + public boolean isInit() { + return init.get(); + } + } + + private static class InitializableSupplierWrappingInstanceBinding extends InitializableSupplierInstanceBinding { + private final SupplierInstanceBinding wrapped; + public InitializableSupplierWrappingInstanceBinding(SupplierInstanceBinding binding) { + super(binding.getSupplier()); + wrapped = binding; + } + + private InitializableSupplierWrappingInstanceBinding(InitializableSupplierWrappingInstanceBinding binding) { + super(binding.getOriginalSupplier()); + wrapped = binding.wrapped; + } + + @Override + public Class getImplementationType() { + return super.getImplementationType(); + } + + @Override + public Supplier getSupplier() { + return super.getSupplier(); + } + + @Override + public Class getScope() { + return wrapped.getScope(); + } + + @Override + public Set getContracts() { + return wrapped.getContracts(); + } + + @Override + public Integer getRank() { + return wrapped.getRank(); + } + + @Override + public Set getAliases() { + return wrapped.getAliases(); + } + + @Override + public Set getQualifiers() { + return wrapped.getQualifiers(); + } + + @Override + public String getAnalyzer() { + return wrapped.getAnalyzer(); + } + + @Override + public String getName() { + return wrapped.getName(); + } + + @Override + public InitializableSupplierWrappingInstanceBinding clone() { + return new InitializableSupplierWrappingInstanceBinding(this); + } + + @Override + public String toString() { + return "InitializableSupplierWrappingInstanceBinding(" + wrapped.getSupplier() + ")"; + } + } + +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/inject/MatchableBinding.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/inject/MatchableBinding.java new file mode 100644 index 0000000000..e6cd0bd34f --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/inject/MatchableBinding.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.inject; + +import org.glassfish.jersey.internal.inject.Binding; + +import java.lang.reflect.Type; +import java.util.Set; + +/** + * A Binding to be able to be compared and matched in the runtime to be properly initialized. + * + * @param Type of the bean described by this injection binding. + * @param Concrete injection binding implementation type. + */ +public abstract class MatchableBinding extends Binding { + + protected abstract MatchLevel bestMatchLevel(); + + protected Matching matches(Binding other) { + final Matching matching = matchesContracts(other); + if (matching.matchLevel == MatchLevel.FULL_CONTRACT) { + if (getImplementationType().equals(other.getImplementationType())) { + matching.matchLevel = MatchLevel.IMPLEMENTATION; + } + } + return matching; + } + + /** + * Return a Matching object that represents comparison between contracts of this binding and a given binding. + * The result contains a reference to this binding. + * @param other + * @return + */ + public Matching matchesContracts(Binding other) { + boolean atLeastOneMatch = false; + boolean allMatch = true; + final Set firstContracts = getContracts(); + final Set secondContracts = other.getContracts(); + final Set biggerContracts = firstContracts.size() < secondContracts.size() ? secondContracts : firstContracts; + final Set smallerContracts = firstContracts.size() < secondContracts.size() ? firstContracts : secondContracts; + + for (Type thisType : biggerContracts) { + boolean aMatch = false; + for (Type otherType : smallerContracts) { + if (thisType.equals(otherType)) { + aMatch = true; + atLeastOneMatch = true; + break; + } + } + if (!aMatch) { + allMatch = false; + } + } + final MatchLevel matchLevel = atLeastOneMatch + ? (allMatch ? MatchLevel.FULL_CONTRACT : MatchLevel.PARTIAL_CONTRACT) + : MatchLevel.NONE; + final Matching matching = new Matching<>((D) this, matchLevel); + return matching; + } + + /** + * Matching object that represents the level of a match between two bindings. Contains a reference to a MatchableBinding + * whose instance was used to create the Matching object. + * @param Concrete injection binding implementation type. + */ + public static class Matching implements Comparable { + private D binding; + protected MatchLevel matchLevel; + + public static Matching noneMatching() { + return new Matching(null, MatchLevel.NONE); + } + + protected Matching(D binding, MatchLevel matchLevel) { + this.binding = binding; + this.matchLevel = matchLevel; + } + + @Override + public int compareTo(Matching other) { + return other.matchLevel.level - this.matchLevel.level; + } + + public Matching better(Matching other) { + return compareTo(other) <= 0 ? this : other; + } + + public boolean isBest() { + return matches() && matchLevel == binding.bestMatchLevel(); + } + + public boolean matches() { + return matchLevel.level > MatchLevel.NONE.level; + } + + public D getBinding() { + return binding; + } + } + + /** + * Internal granularity of a Matching. + */ + protected static enum MatchLevel { + NONE(0), + PARTIAL_CONTRACT(1), + FULL_CONTRACT(2), + IMPLEMENTATION(3), + SUPPLIER(4); + + private final int level; + + MatchLevel(int level) { + this.level = level; + } + } +} + diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/AbstractInjectionTarget.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/AbstractInjectionTarget.java new file mode 100644 index 0000000000..9b79ffacdc --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/AbstractInjectionTarget.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import java.util.Set; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.inject.spi.InjectionTarget; + +/** + * Abstract class which implements all methods from {@link InjectionTarget} by invoking the same methods on the delegate object. + * Useful super class to extend and override only the needed method. + * + * @param type of the injection target. + * @author Petr Bouda + */ +abstract class AbstractInjectionTarget implements InjectionTarget { + + /** + * Object on which all calls will be delegated. + * + * @return injection target. + */ + abstract InjectionTarget delegate(); + + @Override + public void inject(final T instance, final CreationalContext ctx) { + delegate().inject(instance, ctx); + } + + @Override + public void postConstruct(final T instance) { + delegate().postConstruct(instance); + } + + @Override + public void preDestroy(final T instance) { + delegate().preDestroy(instance); + } + + @Override + public T produce(final CreationalContext ctx) { + return delegate().produce(ctx); + } + + @Override + public void dispose(final T instance) { + delegate().dispose(instance); + } + + @Override + public Set getInjectionPoints() { + return delegate().getInjectionPoints(); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/CachedConstructorAnalyzer.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/CachedConstructorAnalyzer.java new file mode 100644 index 0000000000..2042642287 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/CachedConstructorAnalyzer.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collection; +import java.util.logging.Logger; + +import javax.enterprise.inject.InjectionException; +import javax.inject.Inject; + +import org.glassfish.jersey.internal.LocalizationMessages; +import org.glassfish.jersey.internal.util.collection.LazyValue; +import org.glassfish.jersey.internal.util.collection.Value; +import org.glassfish.jersey.internal.util.collection.Values; + +/** + * Processes a provided class and selects the valid constructor with the largest number of parameters. Constructor is cached + * for a later retrieve. + */ +public class CachedConstructorAnalyzer { + + private static final Logger LOGGER = Logger.getLogger(CachedConstructorAnalyzer.class.getName()); + + private final LazyValue> constructor; + private final Collection> resolverAnnotations; + + /** + * Creates a new constructor analyzer which accepts the class that is analyzed. + * + * @param clazz analyzed class. + * @param annotations all annotations used for an injecting. + */ + public CachedConstructorAnalyzer(Class clazz, Collection> annotations) { + this.resolverAnnotations = annotations; + this.constructor = Values.lazy((Value>) () -> getConstructorInternal(clazz)); + } + + public Constructor getConstructor() { + return constructor.get(); + } + + public boolean hasCompatibleConstructor() { + try { + return constructor.get() != null; + } catch (InjectionException ex) { + // Compatible constructor was not found. + return false; + } + } + + /** + * Select the proper constructor of the given {@code clazz}. + * + * @return compatible and largest constructor. + */ + @SuppressWarnings("unchecked") + private Constructor getConstructorInternal(Class clazz) { + if (clazz.isLocalClass()) { + throw new InjectionException( + LocalizationMessages.INJECTION_ERROR_LOCAL_CLASS_NOT_SUPPORTED(clazz.getName())); + } + if (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers())) { + throw new InjectionException( + LocalizationMessages.INJECTION_ERROR_NONSTATIC_MEMBER_CLASS_NOT_SUPPORTED(clazz.getName())); + } + + // At this point, we simply need to find the constructor with the largest number of parameters + Constructor[] constructors = AccessController.doPrivileged( + (PrivilegedAction[]>) clazz::getDeclaredConstructors); + Constructor selected = null; + int selectedSize = 0; + int maxParams = -1; + + for (Constructor constructor : constructors) { + Class[] params = constructor.getParameterTypes(); + if (params.length >= maxParams && isCompatible(constructor)) { + if (params.length > maxParams) { + maxParams = params.length; + selectedSize = 0; + } + + selected = constructor; + selectedSize++; + } + } + + if (selectedSize == 0) { + throw new InjectionException(LocalizationMessages.INJECTION_ERROR_SUITABLE_CONSTRUCTOR_NOT_FOUND(clazz.getName())); + } + + if (selectedSize > 1) { + // Found {0} constructors with {1} parameters in {2} class. Selecting the first found constructor: {3} + LOGGER.warning(LocalizationMessages.MULTIPLE_MATCHING_CONSTRUCTORS_FOUND( + selectedSize, maxParams, clazz.getName(), selected.toGenericString())); + } + + return (Constructor) selected; + } + + /** + * Checks whether the constructor is valid for injection that means that all parameters has an injection annotation. + * + * @param constructor constructor to inject. + * @return True if element contains at least one inject annotation. + */ + @SuppressWarnings("MagicConstant") + private boolean isCompatible(Constructor constructor) { + if (constructor.getAnnotation(Inject.class) != null) { + // JSR-330 applicable + return true; + } + + int paramSize = constructor.getParameterTypes().length; + if (paramSize != 0 && resolverAnnotations.isEmpty()) { + return false; + } + + if (!Modifier.isPublic(constructor.getModifiers())) { + // return true for a default constructor, return false otherwise. + return paramSize == 0 + && (constructor.getDeclaringClass().getModifiers() + & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE)) == constructor.getModifiers(); + } + + for (Annotation[] paramAnnotations : constructor.getParameterAnnotations()) { + boolean found = false; + for (Annotation paramAnnotation : paramAnnotations) { + if (resolverAnnotations.contains(paramAnnotation.annotationType())) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + + return true; + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/Collector.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/Collector.java new file mode 100644 index 0000000000..97077b23a9 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/Collector.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import java.util.LinkedHashSet; +import java.util.LinkedList; + +/** + * This class collects errors, and can then also produce a MultiException from those errors if necessary. + * + * @author John Wells (john.wells at oracle.com) + */ +class Collector { + + private LinkedHashSet throwables; + + /** + * Merges {@link MultiException} with all {@code throwables} registered in it. + * + * @param me {@code MultiException} to merge. + */ + public void addMultiException(MultiException me) { + if (me == null) { + return; + } + if (throwables == null) { + throwables = new LinkedHashSet<>(); + } + + throwables.addAll(me.getErrors()); + } + + /** + * Adds a throwable to the list of throwables in this collector. + * + * @param th The throwable to add to the list. + */ + public void addThrowable(Throwable th) { + if (th == null) { + return; + } + if (throwables == null) { + throwables = new LinkedHashSet<>(); + } + + if (th instanceof MultiException) { + throwables.addAll(((MultiException) th).getErrors()); + } else { + throwables.add(th); + } + } + + /** + * This method will throw if the list of throwables associated with this collector is not empty. + * + * @throws MultiException An exception with all the throwables found in this collector. + */ + public void throwIfErrors() throws MultiException { + if (throwables == null || throwables.isEmpty()) { + return; + } + + throw new MultiException(new LinkedList<>(throwables)); + } + + /** + * Returns true if this collector has errors. + * + * @return true if the collector has errors. + */ + public boolean hasErrors() { + return ((throwables != null) && (!throwables.isEmpty())); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/ContextInjectionResolverImpl.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/ContextInjectionResolverImpl.java new file mode 100644 index 0000000000..b494906333 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/ContextInjectionResolverImpl.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import java.lang.reflect.Type; +import java.util.Set; +import java.util.function.Supplier; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.GenericType; + +import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.Bindings; +import org.glassfish.jersey.internal.inject.ContextInjectionResolver; +import org.glassfish.jersey.internal.inject.Injectee; +import org.glassfish.jersey.internal.inject.InjecteeImpl; +import org.glassfish.jersey.internal.inject.InjectionResolver; +import org.glassfish.jersey.internal.util.ReflectionHelper; +import org.glassfish.jersey.internal.util.collection.Cache; + +/** + * Injection resolver for {@link Context @Context} injection annotation. + * + * @author Petr Bouda + */ +public class ContextInjectionResolverImpl implements InjectionResolver, ContextInjectionResolver { + + private Supplier beanManager; + + /** + * Creates a new {@link ContextInjectionResolver} with {@link BeanManager} to fetch Bean descriptors. + * + * @param beanManager current bean manager. + */ + ContextInjectionResolverImpl(Supplier beanManager) { + this.beanManager = beanManager; + } + + private final Cache> descriptorCache = new Cache<>(key -> { + Set> beans = beanManager.get().getBeans(key); + if (beans.isEmpty()) { + return null; + } + return beans.iterator().next(); + }); + + @Override + public Object resolve(Injectee injectee) { + Injectee newInjectee = injectee; + if (injectee.isFactory()) { + newInjectee = getFactoryInjectee(injectee, ReflectionHelper.getTypeArgument(injectee.getRequiredType(), 0)); + } + + Bean bean = descriptorCache.apply(newInjectee.getRequiredType()); + + if (bean != null) { + CreationalContext ctx = beanManager.get().createCreationalContext(bean); + Object result = bean.create(ctx); + + if (injectee.isFactory()) { + return (Supplier) () -> result; + } else { + return result; + } + } + return null; + } + + @Override + public boolean isConstructorParameterIndicator() { + return true; + } + + @Override + public boolean isMethodParameterIndicator() { + return false; + } + + @Override + public Class getAnnotation() { + return Context.class; + } + + /** + * Context injection resolver binder. + */ + public static final class Binder extends AbstractBinder { + + private Supplier beanManager; + + public Binder(Supplier beanManager) { + this.beanManager = beanManager; + } + + @Override + @SuppressWarnings("unchecked") + protected void configure() { + ContextInjectionResolverImpl resolver = new ContextInjectionResolverImpl(beanManager); + + /* + * Binding for CDI, without this binding JerseyInjectionTarget wouldn't know about the ContextInjectionTarget and + * injection into fields would be disabled. + */ + bind(resolver) + .to(new GenericType>() {}) + .to(ContextInjectionResolver.class); + + /* + * Binding for Jersey, without this binding Jersey wouldn't put together ContextInjectionResolver and + * DelegatedInjectionValueParamProvider and therefore injection into resource method would be disabled. + */ + bind(Bindings.service(resolver)) + .to(new GenericType>() {}) + .to(ContextInjectionResolver.class); + } + } + + private Injectee getFactoryInjectee(Injectee injectee, Type requiredType) { + return new RequiredTypeOverridingInjectee(injectee, requiredType); + } + + private static class RequiredTypeOverridingInjectee extends InjecteeImpl { + private RequiredTypeOverridingInjectee(Injectee injectee, Type requiredType) { + setFactory(injectee.isFactory()); + setInjecteeClass(injectee.getInjecteeClass()); + setInjecteeDescriptor(injectee.getInjecteeDescriptor()); + setOptional(injectee.isOptional()); + setParent(injectee.getParent()); + setPosition(injectee.getPosition()); + setRequiredQualifiers(injectee.getRequiredQualifiers()); + setRequiredType(requiredType); + } + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/InjectionUtils.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/InjectionUtils.java new file mode 100644 index 0000000000..946db4d7ed --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/InjectionUtils.java @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import javax.enterprise.inject.spi.Bean; +import javax.inject.Named; +import javax.inject.Provider; + +import org.glassfish.jersey.internal.inject.Injectee; +import org.glassfish.jersey.internal.inject.InjecteeImpl; +import org.glassfish.jersey.internal.inject.InjectionResolver; +import org.glassfish.jersey.internal.util.Pretty; +import org.glassfish.jersey.internal.util.collection.ImmutableCollectors; + +/** + * Utility class for processing of an injection. + * + * @author Petr Bouda + */ +public final class InjectionUtils { + + /** + * Forbids the creation of {@link InjectionUtils} instance. + */ + private InjectionUtils() { + } + + /** + * Just injects the thing, doesn't try to do anything else + * + * @param injectMe the object to inject into. + * @param bean information about the injected instance. + * @param resolvers all injection resolvers registered in the application. + * @param proxyResolver object which is able to create a proxy. + * @param type of the injected instance. + */ + static void justInject(T injectMe, Bean bean, Map resolvers, + JerseyProxyResolver proxyResolver) { + if (injectMe == null) { + throw new IllegalArgumentException(); + } + + for (Map.Entry entry : resolvers.entrySet()) { + Field field = entry.getKey(); + InjectionResolver resolver = entry.getValue(); + Injectee injectee = InjectionUtils.getFieldInjectee(bean, field); + + Object resolvedValue; + if (injectee.isProvider()) { + resolvedValue = (Provider) () -> resolver.resolve(injectee); + } else if (proxyResolver.isProxiable(injectee)) { + resolvedValue = proxyResolver.proxy(injectee, resolver); + } else { + resolvedValue = resolver.resolve(injectee); + } + + try { + ReflectionUtils.setField(field, injectMe, resolvedValue); + } catch (MultiException me) { + throw me; + } catch (Throwable th) { + throw new MultiException(th); + } + } + } + + /** + * Returns the injectee for a field. + * + * @param bean bean in which the field is placed. + * @param field the field to analyze. + * @return the list (in order) of parameters to the constructor. + */ + private static Injectee getFieldInjectee(Bean bean, Field field) { + Set annotations = Arrays.stream(field.getAnnotations()) + .collect(ImmutableCollectors.toImmutableSet()); + + Type adjustedType = ReflectionUtils.resolveField(bean.getBeanClass(), field); + + InjecteeImpl injectee = new InjecteeImpl(); + injectee.setParentClassScope(bean.getScope()); + + if (isProvider(adjustedType)) { + ParameterizedType paramType = (ParameterizedType) adjustedType; + injectee.setRequiredType(paramType.getActualTypeArguments()[0]); + injectee.setProvider(true); + } else { + injectee.setRequiredType(adjustedType); + } + + injectee.setParent(field); + injectee.setRequiredQualifiers(getFieldAdjustedQualifierAnnotations(field, annotations)); + return injectee; + } + + public static boolean isProvider(Type type) { + if (type instanceof ParameterizedType) { + ParameterizedType paramType = (ParameterizedType) type; + return Provider.class.isAssignableFrom((Class) paramType.getRawType()); + } + + return false; + } + + private static Set getFieldAdjustedQualifierAnnotations(Field field, Set qualifiers) { + Named n = field.getAnnotation(Named.class); + if (n == null || !"".equals(n.value())) { + return qualifiers; + } + + HashSet retVal = new HashSet<>(); + for (Annotation qualifier : qualifiers) { + if (qualifier.annotationType().equals(Named.class)) { + retVal.add(new NamedImpl(field.getName())); + } else { + retVal.add(qualifier); + } + } + + return retVal; + } + + /** + * Gets the fields from the given class and analyzer. Checks service output. + * + * @param clazz the non-null impl class. + * @param injectAnnotations all annotations which can be used to inject a value. + * @param errors for gathering errors. @return a non-null set (even in error cases, check the collector). + */ + static Set getFields(Class clazz, Set> injectAnnotations, Collector errors) { + Set retVal; + + try { + retVal = getFieldsInternal(clazz, injectAnnotations, errors); + } catch (MultiException me) { + errors.addMultiException(me); + return Collections.emptySet(); + } catch (Throwable th) { + errors.addThrowable(th); + return Collections.emptySet(); + } + + return retVal; + } + + /** + * Will find all the initialize fields in the class. + * + * @param clazz the class to search for fields + * @param injectAnnotations all annotations which can be used to inject a value. + * @param errors the error collector + * @return A non-null but possibly empty set of initializer fields + */ + private static Set getFieldsInternal(Class clazz, Set> injectAnnotations, Collector errors) { + Set retVal = new LinkedHashSet<>(); + + for (Field field : ReflectionUtils.getAllFields(clazz)) { + if (!hasInjectAnnotation(field, injectAnnotations)) { + // Not an initializer field + continue; + } + + if (!isProperField(field)) { + errors.addThrowable(new IllegalArgumentException("The field " + Pretty.field(field) + + " may not be static, final or have an Annotation type")); + continue; + } + + retVal.add(field); + } + + return retVal; + } + + /** + * Checks whether an annotated element has any annotation that was used for the injection. + * + * @param annotated the annotated element. + * @param injectAnnotations all annotations which can be used to inject a value. + * @return True if element contains at least one inject annotation. + */ + private static boolean hasInjectAnnotation(AnnotatedElement annotated, Set> injectAnnotations) { + for (Annotation annotation : annotated.getAnnotations()) { + if (injectAnnotations.contains(annotation.annotationType())) { + return true; + } + } + + return false; + } + + private static boolean isProperField(Field field) { + if (isStatic(field) || isFinal(field)) { + return false; + } + + Class type = field.getType(); + return !type.isAnnotation(); + } + + /** + * Returns true if the underlying member is static. + * + * @param member The non-null member to test. + * @return true if the member is static. + */ + private static boolean isStatic(Member member) { + int modifiers = member.getModifiers(); + return ((modifiers & Modifier.STATIC) != 0); + } + + /** + * Returns true if the underlying member is abstract. + * + * @param member The non-null member to test. + * @return true if the member is abstract. + */ + private static boolean isFinal(Member member) { + int modifiers = member.getModifiers(); + return ((modifiers & Modifier.FINAL) != 0); + } + + /** + * Returns all annotations that can be managed using registered and provided {@link InjectionResolver injection resolvers}. + * + * @param resolvers all registered resolvers. + * @return all possible injection annotations. + */ + @SuppressWarnings("unchecked") + public static Collection> getInjectAnnotations(Collection resolvers) { + List> annotations = new ArrayList<>(); + for (InjectionResolver resolver : resolvers) { + annotations.add(resolver.getAnnotation()); + } + return annotations; + } + + /** + * Assigns {@link InjectionResolver} to every {@link AnnotatedElement} provided as a method parameter. Injection resolver + * will be used for fetching the proper value during the injection processing. + * + * @param annotatedElements all annotated elements from the class which this injector belongs to. + * @param resolvers all registered injection resolvers. + * @param type of the annotated elements. + * @return immutable map of all fields along with injection resolvers using that can be injected. + */ + static Map mapElementToResolver(Set annotatedElements, + Map, InjectionResolver> resolvers) { + + Map mappedElements = new HashMap<>(); + for (A element : annotatedElements) { + mappedElements.put(element, findResolver(resolvers, element)); + } + return mappedElements; + } + + static InjectionResolver findResolver(Map, InjectionResolver> resolvers, AnnotatedElement element) { + for (Annotation annotation : element.getAnnotations()) { + InjectionResolver injectionResolver = resolvers.get(annotation.annotationType()); + if (injectionResolver != null) { + return injectionResolver; + } + } + + return null; + } + + /** + * Creates a map from resolvers where the annotation that is handled by resolver is a key and resolver itself is value. + * + * @param resolvers collection of resolvers. + * @return map resolver annotation to resolver. + */ + static Map, InjectionResolver> mapAnnotationToResolver(Collection resolvers) { + return resolvers.stream().collect(Collectors.toMap(InjectionResolver::getAnnotation, Function.identity())); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyClientCreationalContext.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyClientCreationalContext.java new file mode 100644 index 0000000000..529b94f177 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyClientCreationalContext.java @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import javax.enterprise.context.spi.Contextual; +import javax.enterprise.context.spi.CreationalContext; + +import org.glassfish.jersey.inject.weld.internal.managed.CdiClientInjectionManager; +import org.jboss.weld.construction.api.AroundConstructCallback; +import org.jboss.weld.context.api.ContextualInstance; +import org.jboss.weld.contexts.CreationalContextImpl; +import org.jboss.weld.injection.spi.ResourceReference; +import org.jboss.weld.interceptor.proxy.InterceptionContext; + +import java.util.List; + +/** + * Jersey implementation of CreationalContext holding an instance of the client InjectionManager. + * Should be used on the client side only. Wraps the original context. + * @param the class of the creational context. + */ +public class JerseyClientCreationalContext extends CreationalContextImpl { + + private final CreationalContextImpl wrapped; + private CdiClientInjectionManager injectionManager = null; + + public JerseyClientCreationalContext(CreationalContextImpl wrapped) { + super(wrapped.getContextual()); + this.wrapped = wrapped; + } + + @Override + public CreationalContextImpl getCreationalContext(Contextual contextual) { + return new JerseyClientCreationalContext<>(wrapped.getCreationalContext(contextual)) + .setInjectionManager(injectionManager); + } + + public CreationalContextImpl getProducerReceiverCreationalContext(Contextual contextual) { + return new JerseyClientCreationalContext<>(wrapped.getProducerReceiverCreationalContext(contextual)) + .setInjectionManager(injectionManager); + } + + public S getIncompleteInstance(Contextual bean) { + return wrapped.getIncompleteInstance(bean); + } + + public boolean containsIncompleteInstance(Contextual bean) { + return wrapped.containsIncompleteInstance(bean); + } + + public void addDependentInstance(ContextualInstance contextualInstance) { + wrapped.addDependentInstance(contextualInstance); + } + + public void release() { + wrapped.release(); + } + + public void release(Contextual contextual, T instance) { + wrapped.release(contextual, instance); + } + + /** + * @return the parent {@link CreationalContext} or null if there isn't any parent. + */ + public CreationalContextImpl getParentCreationalContext() { + return wrapped.getParentCreationalContext(); + } + + /** + * Returns an unmodifiable list of dependent instances. + */ + public List> getDependentInstances() { + return wrapped.getDependentInstances(); + } + + /** + * Register a {@link ResourceReference} as a dependency. {@link ResourceReference#release()} will be called on every {@link ResourceReference} once this + * {@link CreationalContext} instance is released. + */ + public void addDependentResourceReference(ResourceReference resourceReference) { + wrapped.addDependentResourceReference(resourceReference); + } + + /** + * Destroys dependent instance + * + * @param instance + * @return true if the instance was destroyed, false otherwise + */ + public boolean destroyDependentInstance(T instance) { + return wrapped.destroyDependentInstance(instance); + } + + /** + * @return the {@link Contextual} for which this {@link CreationalContext} is created. + */ + public Contextual getContextual() { + return wrapped.getContextual(); + } + + public List> getAroundConstructCallbacks() { + return wrapped.getAroundConstructCallbacks(); + } + + @Override + public void setConstructorInterceptionSuppressed(boolean value) { + wrapped.setConstructorInterceptionSuppressed(value); + } + + @Override + public boolean isConstructorInterceptionSuppressed() { + return wrapped.isConstructorInterceptionSuppressed(); + } + + @Override + public void registerAroundConstructCallback(AroundConstructCallback callback) { + wrapped.registerAroundConstructCallback(callback); + } + + /** + * + * @return the interception context used for Weld-managed AroundConstruct interceptors or null if no such interceptors were applied + */ + public InterceptionContext getAroundConstructInterceptionContext() { + return wrapped.getAroundConstructInterceptionContext(); + } + + public void setAroundConstructInterceptionContext(InterceptionContext aroundConstructInterceptionContext) { + wrapped.setAroundConstructInterceptionContext(aroundConstructInterceptionContext); + } + + public JerseyClientCreationalContext setInjectionManager(CdiClientInjectionManager injectionManager) { + this.injectionManager = injectionManager; + return this; + } + + public CdiClientInjectionManager getInjectionManager() { + return injectionManager; + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyConstructorInjectionPoint.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyConstructorInjectionPoint.java new file mode 100644 index 0000000000..d2677ac3ca --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyConstructorInjectionPoint.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Parameter; +import java.lang.reflect.ParameterizedType; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.Bean; +import javax.inject.Provider; + +import org.glassfish.jersey.internal.inject.Injectee; +import org.glassfish.jersey.internal.inject.InjecteeImpl; +import org.glassfish.jersey.internal.inject.InjectionResolver; + +import org.jboss.weld.annotated.enhanced.EnhancedAnnotatedConstructor; +import org.jboss.weld.injection.ConstructorInjectionPoint; +import org.jboss.weld.injection.InjectionPointFactory; +import org.jboss.weld.injection.ParameterInjectionPoint; +import org.jboss.weld.manager.BeanManagerImpl; + +/** + * Class that creates a new instance using the provided constructor, selects and injects the values. + * + * @author Petr Bouda + */ +public class JerseyConstructorInjectionPoint extends ConstructorInjectionPoint { + + private final JerseyProxyResolver proxyResolver = new JerseyProxyResolver(); + + private List> cachedSuppliers; + + private Object[] cachedProxies; + + /** + * Creates a new constructor injection point suitable for Jersey components. + * + * @param constructor resolved constructor that can be injected using Jersey. + * @param bean bean descriptor dedicated to the parent class. + * @param manager current bean manager. + * @param resolvers all registered resolvers. + */ + public JerseyConstructorInjectionPoint(EnhancedAnnotatedConstructor constructor, Bean bean, BeanManagerImpl manager, + Collection resolvers) { + super(constructor, null, constructor.getJavaClass(), InjectionPointFactory.instance(), manager); + + List valueSuppliers = + createValueSuppliers(constructor.getJavaMember(), getParameterInjectionPoints(), resolvers); + + /* + * Caches either created proxies if the component class is not RequestScoped or caches the supplier that just create + * values every component creates. + */ + if (proxyResolver.isProxiable(bean.getScope())) { + this.cachedProxies = generateProxies(valueSuppliers); + } else { + this.cachedSuppliers = valueSuppliers.stream() + .map(is -> is.supplier) + .collect(Collectors.toList()); + } + } + + /** + * Helper method for getting the current parameter values from a list of annotated parameters. + * + * @param manager The Bean manager + * @return The object array of looked up values + */ + public Object[] getParameterValues(BeanManagerImpl manager, CreationalContext ctx, CreationalContext ctxTransient) { + if (cachedProxies == null) { + return generateValues(cachedSuppliers); + } else { + return cachedProxies; + } + } + + private Object[] generateValues(List> suppliers) { + Object[] parameterValues = new Object[getParameterInjectionPoints().size()]; + for (int i = 0; i < parameterValues.length; i++) { + parameterValues[i] = suppliers.get(i).get(); + } + return parameterValues; + } + + private Object[] generateProxies(List suppliers) { + Object[] proxies = new Object[suppliers.size()]; + for (int i = 0; i < proxies.length; i++) { + InjecteeToSupplier injecteeToSupplier = suppliers.get(i); + if (injecteeToSupplier.injectee.isProvider()) { + proxies[i] = new Provider() { + @Override + public Object get() { + return injecteeToSupplier.supplier.get(); + } + }; + } else { + proxies[i] = proxyResolver.noCachedProxy(injecteeToSupplier.injectee, injecteeToSupplier.supplier); + } + } + return proxies; + } + + /** + * Maps the parameters of the selected constructor to the injection resolver. + * + * @param params all parameters of a constructor. + * @param resolvers registered injection resolvers. + * @return map of the parameter to injection resolver. + */ + private List createValueSuppliers(Constructor constructor, + List> params, Collection resolvers) { + + List suppliers = new ArrayList<>(); + Map, InjectionResolver> injectAnnotations = InjectionUtils.mapAnnotationToResolver(resolvers); + for (int i = 0; i < params.size(); i++) { + Parameter parameter = params.get(i).getAnnotated().getJavaParameter(); + InjectionResolver resolver = InjectionUtils.findResolver(injectAnnotations, parameter); + Injectee injectee = parameterToInjectee(constructor, parameter, i); +// if (!Class.class.isInstance(injectee.getRequiredType()) || ((Class) injectee.getRequiredType()).isInterface()) { + suppliers.add(new InjecteeToSupplier(injectee, () -> resolver.resolve(injectee))); +// } + } + + return suppliers; + } + + private Injectee parameterToInjectee(Constructor constructor, Parameter parameter, int position) { + InjecteeImpl injectee = new InjecteeImpl(); + injectee.setParent(constructor); + if (parameter.getParameterizedType() instanceof ParameterizedType + && InjectionUtils.isProvider(parameter.getParameterizedType())) { + ParameterizedType paramType = (ParameterizedType) parameter.getParameterizedType(); + injectee.setRequiredType(paramType.getActualTypeArguments()[0]); + injectee.setProvider(true); + } else { + injectee.setRequiredType(parameter.getType()); + } + injectee.setPosition(position); + return injectee; + } + + /** + * Holder for Injectee and Supplier types. Internal class. + */ + private static class InjecteeToSupplier { + + private final Injectee injectee; + private final Supplier supplier; + + private InjecteeToSupplier(Injectee injectee, Supplier supplier) { + this.injectee = injectee; + this.supplier = supplier; + } + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyInjectionTarget.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyInjectionTarget.java new file mode 100644 index 0000000000..e5702c0c15 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyInjectionTarget.java @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.List; + +import javax.enterprise.context.Dependent; +import javax.ws.rs.WebApplicationException; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.InjectionException; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.Decorator; +import javax.enterprise.inject.spi.InjectionTarget; +import javax.enterprise.inject.spi.Interceptor; + +import org.glassfish.jersey.inject.weld.internal.bean.BeanHelper; +import org.glassfish.jersey.inject.weld.internal.bean.JerseyBean; +import org.glassfish.jersey.internal.inject.InjectionResolver; +import org.glassfish.jersey.internal.util.collection.LazyValue; +import org.glassfish.jersey.internal.util.collection.Value; +import org.glassfish.jersey.internal.util.collection.Values; + +import org.jboss.weld.annotated.enhanced.EnhancedAnnotatedConstructor; +import org.jboss.weld.annotated.enhanced.EnhancedAnnotatedMethod; +import org.jboss.weld.annotated.enhanced.EnhancedAnnotatedType; +import org.jboss.weld.bean.CustomDecoratorWrapper; +import org.jboss.weld.bean.DecoratorImpl; +import org.jboss.weld.bean.proxy.ProxyInstantiator; +import org.jboss.weld.injection.producer.AbstractInstantiator; +import org.jboss.weld.injection.producer.BasicInjectionTarget; +import org.jboss.weld.injection.producer.ConstructorInterceptionInstantiator; +import org.jboss.weld.injection.producer.DefaultInstantiator; +import org.jboss.weld.injection.producer.Instantiator; +import org.jboss.weld.injection.producer.InterceptionModelInitializer; +import org.jboss.weld.injection.producer.InterceptorApplyingInstantiator; +import org.jboss.weld.injection.producer.SubclassDecoratorApplyingInstantiator; +import org.jboss.weld.injection.producer.SubclassedComponentInstantiator; +import org.jboss.weld.interceptor.spi.model.InterceptionModel; +import org.jboss.weld.logging.BeanLogger; +import org.jboss.weld.resources.ClassTransformer; +import org.jboss.weld.util.reflection.Formats; + +/** + * Wrapper for {@link InjectionTarget} that implements the functionality of injecting using JAX-RS annotations into provided + * instances. {@code Delegate} is a original {@code InjectionTarget} which is able to inject other fields/parameters which + * are managed by CDI. + *

+ * Implementation is also able create with custom {@code jerseyConstructor} if it is provided. This functionality allows override + * default instantiator and use the Jersey-specific one. + */ +public class JerseyInjectionTarget extends BasicInjectionTarget { + + private final Bean bean; + private final Class clazz; + private final LazyValue> injector; + private final EnhancedAnnotatedType enhancedAnnotatedType; + private Collection resolvers; + private BasicInjectionTarget delegate; // for managed beans the initializeAfterBeanDiscovery is called for it + private final Instantiator instantiator; + + /** + * Creates a new injection target which is able to delegate an injection to {@code delegate injection target} and inject + * the fields that are Jersey-specific. The resolvers must be set later on. CDI will select its own constructor. + * + * @param delegate CDI specific injection target. + * @param clazz class that will be scanned and injected. + */ + public JerseyInjectionTarget(BasicInjectionTarget delegate, Class clazz) { + this(delegate, delegate.getBean(), clazz, null); + } + + /** + * Creates a new injection target which is able to delegate an injection to {@code delegate injection target} and inject + * the fields that are Jersey-specific. CDI will select its own constructor. + * + * @param delegate CDI specific injection target. + * @param bean bean which this injection target belongs to. + * @param clazz class that will be scanned and injected. + * @param resolvers all resolvers that can provide a valued for Jersey-specific injection. + */ + public JerseyInjectionTarget(BasicInjectionTarget delegate, Bean bean, Class clazz, + Collection resolvers) { + this(BeanHelper.createEnhancedAnnotatedType(delegate), delegate, bean, clazz, resolvers, delegate.getInstantiator()); + this.delegate = delegate; + setInstantiator(this.instantiator); + } + + /** + * Creates a new injection target which is able to delegate an injection to {@code delegate injection target} and inject + * the fields that are Jersey-specific. This method accepts custom instantiator, if the instantiator is {@code null} + * default one is created. + * + * @param annotatedType resolved type of the registered bean. + * @param delegate CDI specific injection target. + * @param bean bean which this injection target belongs to. + * @param clazz class that will be scanned and injected. + * @param resolvers all resolvers that can provide a valued for Jersey-specific injection. + * @param instantiator default instantiator. + */ + public JerseyInjectionTarget(EnhancedAnnotatedType annotatedType, BasicInjectionTarget delegate, Bean bean, + Class clazz, Collection resolvers, Instantiator instantiator) { + super(annotatedType, + bean, + delegate.getBeanManager(), + delegate.getInjector(), + delegate.getLifecycleCallbackInvoker(), + JerseyBean.class.isInstance(bean) + ? new JerseyTwofoldInstantiator((AbstractInstantiator) instantiator) + : instantiator); + + this.bean = bean; + this.enhancedAnnotatedType = annotatedType; + this.clazz = clazz; + this.resolvers = resolvers; + this.injector = Values.lazy((Value>) () -> new JerseyInstanceInjector<>(bean, this.resolvers)); + this.delegate = null; + this.instantiator = getInstantiator(); + setInstantiator(this.instantiator); + } + + @Override + protected void checkDelegateInjectionPoints() { + if (getAnnotatedType().getAnnotation(javax.decorator.Decorator.class) == null) { + super.checkDelegateInjectionPoints(); + } + } + + @Override + public void inject(T instance, CreationalContext ctx) { + /* + * If an instance contains any fields which be injected by Jersey then Jersey attempts to inject them using annotations + * retrieves from registered InjectionResolvers. + */ + try { + injector.get().inject(instance); + } catch (WebApplicationException wae) { + throw wae; + } catch (Throwable cause) { + throw new InjectionException( + "Exception occurred during Jersey/JAX-RS annotations processing in the class: " + clazz, cause); + } + + /* + * The rest of the fields (annotated by @Inject) are injected using CDI. + */ + super.inject(instance, ctx); + } + + /** + * Copied method from the parent class because of a custom type of {@link Instantiator} is used in this implementation. + * + * @param annotatedType processed class. + */ + @Override + public void initializeAfterBeanDiscovery(EnhancedAnnotatedType annotatedType) { + initializeInterceptionModel(annotatedType); + + InterceptionModel interceptionModel = null; + if (isInterceptionCandidate()) { + interceptionModel = beanManager.getInterceptorModelRegistry().get(getType()); + } + boolean hasNonConstructorInterceptors = interceptionModel != null + && (interceptionModel.hasExternalNonConstructorInterceptors() + || interceptionModel.hasTargetClassInterceptors()); + + List> decorators = null; + if (getBean() != null && isInterceptionCandidate()) { + decorators = beanManager.resolveDecorators(getBean().getTypes(), getBean().getQualifiers()); + } + boolean hasDecorators = decorators != null && !decorators.isEmpty(); + if (hasDecorators) { + checkDecoratedMethods(annotatedType, decorators); + } + + if (hasNonConstructorInterceptors || hasDecorators) { + if (!(getInstantiator() instanceof DefaultInstantiator)) { + throw new IllegalStateException("Unexpected instantiator " + getInstantiator()); + } + + /* + * Casting changed from DefaultInstantiator to a more abstract one because of using our custom JerseyInstantiator. + */ + AbstractInstantiator delegate = (AbstractInstantiator) getInstantiator(); + setInstantiator( + SubclassedComponentInstantiator.forInterceptedDecoratedBean(annotatedType, getBean(), delegate, beanManager)); + + if (hasDecorators) { + setInstantiator(new SubclassDecoratorApplyingInstantiator<>( + getBeanManager().getContextId(), getInstantiator(), getBean(), decorators)); + } + + if (hasNonConstructorInterceptors) { + setInstantiator(new InterceptorApplyingInstantiator<>( + getInstantiator(), interceptionModel, getType())); + } + } + + if (isInterceptionCandidate()) { + setupConstructorInterceptionInstantiator(interceptionModel); + } + } + + private void setupConstructorInterceptionInstantiator(InterceptionModel interceptionModel) { + if (interceptionModel != null && interceptionModel.hasExternalConstructorInterceptors()) { + setInstantiator(new ConstructorInterceptionInstantiator<>(getInstantiator(), interceptionModel, getType())); + } + } + + private void checkNoArgsConstructor(EnhancedAnnotatedType type) { + if (!beanManager.getServices().get(ProxyInstantiator.class).isUsingConstructor()) { + return; + } + EnhancedAnnotatedConstructor constructor = type.getNoArgsEnhancedConstructor(); + if (constructor == null) { + throw BeanLogger.LOG.decoratedHasNoNoargsConstructor(this); + } else if (constructor.isPrivate()) { + throw BeanLogger.LOG + .decoratedNoargsConstructorIsPrivate(this, Formats.formatAsStackTraceElement(constructor.getJavaMember())); + } + } + + private void checkDecoratedMethods(EnhancedAnnotatedType type, List> decorators) { + if (type.isFinal()) { + throw BeanLogger.LOG.finalBeanClassWithDecoratorsNotAllowed(this); + } + checkNoArgsConstructor(type); + for (Decorator decorator : decorators) { + EnhancedAnnotatedType decoratorClass; + if (decorator instanceof DecoratorImpl) { + DecoratorImpl decoratorBean = (DecoratorImpl) decorator; + decoratorClass = decoratorBean.getBeanManager().getServices().get(ClassTransformer.class) + .getEnhancedAnnotatedType(decoratorBean.getAnnotated()); + } else if (decorator instanceof CustomDecoratorWrapper) { + decoratorClass = ((CustomDecoratorWrapper) decorator).getEnhancedAnnotated(); + } else { + throw BeanLogger.LOG.nonContainerDecorator(decorator); + } + + for (EnhancedAnnotatedMethod decoratorMethod : decoratorClass.getEnhancedMethods()) { + EnhancedAnnotatedMethod method = type.getEnhancedMethod(decoratorMethod.getSignature()); + if (method != null && !method.isStatic() && !method.isPrivate() && method.isFinal()) { + throw BeanLogger.LOG.finalBeanClassWithInterceptorsNotAllowed(this); + } + } + } + } + + private void initializeInterceptionModel(EnhancedAnnotatedType annotatedType) { + AbstractInstantiator instantiator = (AbstractInstantiator) getInstantiator(); + if (instantiator.getConstructorInjectionPoint() == null) { + return; // this is a non-producible InjectionTarget (only created to inject existing instances) + } + if (isInterceptionCandidate() && !beanManager.getInterceptorModelRegistry().containsKey(getType())) { + buildInterceptionModel(annotatedType, instantiator); + } + } + + private void buildInterceptionModel(EnhancedAnnotatedType annotatedType, AbstractInstantiator instantiator) { + new InterceptionModelInitializer<>(beanManager, annotatedType, annotatedType.getDeclaredEnhancedConstructor( + instantiator.getConstructorInjectionPoint().getSignature()), getBean()).init(); + } + + private boolean isInterceptor() { + return (getBean() instanceof Interceptor) || getType().isAnnotationPresent(javax.interceptor.Interceptor.class); + } + + private boolean isDecorator() { + return (getBean() instanceof Decorator) || getType().isAnnotationPresent(javax.decorator.Decorator.class); + } + + private boolean isInterceptionCandidate() { + return !isInterceptor() && !isDecorator() && !Modifier.isAbstract(getType().getJavaClass().getModifiers()); + } + + @Override + public T produce(CreationalContext ctx) { + T instance; + if (delegate != null) { + instance = (T) delegate.produce(ctx); + } else { + instance = super.produce(ctx); + if (bean != null && !bean.getScope().equals(Dependent.class) && !getInstantiator().hasDecoratorSupport()) { + // This should be safe, but needs verification PLM + // Without this, the chaining of decorators will fail as the + // incomplete instance will be resolved + ctx.push(instance); + } + } + return instance; + } + + public Instantiator getInstantiator() { + return delegate != null ? delegate.getInstantiator() : super.getInstantiator(); + } + + public void setInstantiator(Instantiator instantiator) { + if (this.delegate != null) { + delegate.setInstantiator(instantiator); + } else { + super.setInstantiator(instantiator); + } + } + + @Override + public Bean getBean() { + return this.bean; + } + + /** + * In some cases Injection Resolvers cannot be provided during th creation of the object therefore must be set later on. + * + * @param resolvers all registered injection resolvers. + */ + public void setInjectionResolvers(Collection resolvers) { + this.resolvers = resolvers; + } + + public EnhancedAnnotatedType getEnhancedAnnotatedType() { + return enhancedAnnotatedType; + } + + public JerseyTwofoldInstantiator getTwofoldInstantiator() { + return (JerseyTwofoldInstantiator) instantiator; + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyInstanceInjector.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyInstanceInjector.java new file mode 100644 index 0000000000..07cf0d3eea --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyInstanceInjector.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import javax.enterprise.inject.spi.Bean; + +import org.glassfish.jersey.internal.inject.InjectionResolver; + +/** + * Class that accepts all registered {@link InjectionResolver} and inject all possible values annotated by JAX-RS annotations + * into provided instance in {@link #inject(Object)}. + * + * @author Petr Bouda + */ +class JerseyInstanceInjector { + + private final Bean bean; + private final Map cachedFields; + + private final JerseyProxyResolver proxyResolver = new JerseyProxyResolver(); + + /** + * Constructor that creates a new class injector for the given class. + * + * @param bean information about the injected class. + * @param resolvers all resolvers which are registered in the application. + */ + JerseyInstanceInjector(Bean bean, Collection resolvers) { + this.bean = bean; + this.cachedFields = analyzeFields(bean.getBeanClass(), resolvers); + } + + /** + * Takes an instance an inject the annotated field which were analyzed during the injector construction in method + * {@link #analyzeFields(Class, Collection)}. + * + * @param injectMe an instance into which the values will be injected. + */ + void inject(T injectMe) { + InjectionUtils.justInject(injectMe, bean, cachedFields, proxyResolver); + } + + /** + * Takes a class and returns all fields along with {@link InjectionResolver} which will be used for injection during injection + * process. + * + * @param clazz class to be analyzed. + * @param resolvers all registered injection resolvers. + * @return immutable map of all fields along with injection resolvers using that can be injected. + */ + private Map analyzeFields(Class clazz, Collection resolvers) { + Map, InjectionResolver> injectAnnotations = InjectionUtils.mapAnnotationToResolver(resolvers); + + Collector collector = new Collector(); + Set fields = InjectionUtils.getFields(clazz, injectAnnotations.keySet(), collector); + collector.throwIfErrors(); + return InjectionUtils.mapElementToResolver(fields, injectAnnotations); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyProxyResolver.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyProxyResolver.java new file mode 100644 index 0000000000..20c282c43b --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyProxyResolver.java @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +import javax.ws.rs.core.Application; + +import javax.enterprise.context.RequestScoped; + +import org.glassfish.jersey.internal.inject.Injectee; +import org.glassfish.jersey.internal.inject.InjectionResolver; + +/** + * Class working with JAX-RS/Jersey types injected using {@link javax.ws.rs.core.Context} annotation and all other types which + * can be injected using using other {@code *Param} annotations. + *

+ * Processed JAX-RS interfaces: + * + * @author Petr Bouda + * @see javax.ws.rs.core.UriInfo + * @see javax.ws.rs.core.Request + * @see javax.ws.rs.core.HttpHeaders + * @see javax.ws.rs.core.SecurityContext + * @see javax.ws.rs.core.Configuration + * @see javax.ws.rs.core.Application not proxiable because is registered as a singleton. + * @see javax.ws.rs.ext.Providers + */ +class JerseyProxyResolver { + + /** + * Contains already created proxies for the given type. + * e.g. if the proxy has been already created for {@code UriInfo} don't create a new one and reuse the existing one. + */ + private final ConcurrentHashMap cachedProxies = new ConcurrentHashMap<>(); + + /** + * Classes in which is not needed to use a proxy because they are singletons. + */ + private static final List> IGNORED_CLASSES = Collections.singletonList(Application.class); + + /** + * Returns {@code true} if one of the proxiable annotations is present on the clazz into which values are injected. + *

+ * In these cases the value is not proxiable: + *

    + *
  • Class without the annotation
  • + *
  • Class annotated by {@link javax.enterprise.context.RequestScoped}
  • + *
  • Class annotated by {@link org.glassfish.jersey.process.internal.RequestScoped}
  • + *
      + * + * @param injectee information about the injection point. + * @return {@code true} if contains one proxiable annotation at least. + */ + public boolean isProxiable(Injectee injectee) { + return !ignoredClass(injectee.getRequiredType()) && isProxiable(injectee.getParentClassScope()); + } + + /** + * Returns {@code true} if one of the proxiable annotations is present on the clazz. + *

      + * In these cases the value is not proxiable: + *

        + *
      • Class without the annotation
      • + *
      • Class annotated by {@link javax.enterprise.context.RequestScoped}
      • + *
      • Class annotated by {@link org.glassfish.jersey.process.internal.RequestScoped}
      • + *
          + * + * @param scopeAnnotation annotation belonging to the scope of the class. + * @return {@code true} if contains one proxiable annotation at least. + */ + public boolean isProxiable(Class scopeAnnotation) { + return ignoreProxy().stream().noneMatch(ignoredAnnotation -> ignoredAnnotation == scopeAnnotation); + } + + /** + * Returns a proxy (newly created or cached) which is able to call {@link InjectionResolver} with the given {@link Injectee} + * to get the value in proper scope. + * + * @param injectee information about the injection point. + * @param resolver dedicated resolver which find the value. + * @return created proxy which resolve the value in the proper scope. + */ + public Object proxy(Injectee injectee, InjectionResolver resolver) { + return cachedProxies.computeIfAbsent(injectee.getParent(), type -> createProxy(injectee, resolver)); + } + + /** + * Returns a proxy (newly created or cached) which is able to call the given {@link Supplier}. This method does not cache + * a result. + * + * @param injectee information about the injection point. + * @param supplier supplier called using the proxy. + * @return created proxy which resolve the value in the proper scope. + */ + public Object noCachedProxy(Injectee injectee, Supplier supplier) { + return createProxy(getClass(injectee.getRequiredType()), supplier); + } + + private Object createProxy(Injectee injectee, InjectionResolver resolver) { + return createProxy(getClass(injectee.getRequiredType()), () -> resolver.resolve(injectee)); + } + + private Object createProxy(Class requiredClass, Supplier supplier) { + return Proxy.newProxyInstance( + requiredClass.getClassLoader(), + new Class[] {requiredClass}, + new JerseyInvocationHandler(supplier)); + } + + private Class getClass(Type type) { + if (type instanceof ParameterizedType) { + ParameterizedType paramType = (ParameterizedType) type; + return (Class) paramType.getRawType(); + } + + return (Class) type; + } + + /** + * Returns all annotations for which proxy will be created. + * + * @return all proxyable annotations. + */ + private Collection> ignoreProxy() { + return Arrays.asList(RequestScoped.class, org.glassfish.jersey.process.internal.RequestScoped.class); + } + + /** + * Classes in which is not needed to use a proxy because they are singletons. + * + * @return classes omitted during proxying. + */ + private boolean ignoredClass(Type type) { + Class clazz; + if (type instanceof ParameterizedType) { + ParameterizedType paramType = (ParameterizedType) type; + clazz = (Class) paramType.getRawType(); + } else { + clazz = (Class) type; + } + + return IGNORED_CLASSES.contains(clazz); + } + + /** + * {@link InvocationHandler} to intercept a calling using a proxy by providing a value of the given injected type in a proper + * scope. + */ + private static class JerseyInvocationHandler implements InvocationHandler { + + private final Supplier supplier; + + /** + * Creates a new invocation handler with supplier which provides a current injected value in proper scope. + * + * @param supplier provider of the value. + */ + private JerseyInvocationHandler(Supplier supplier) { + this.supplier = supplier; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Object target = supplier.get(); + return method.invoke(target, args); + } + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyTwofoldInstantiator.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyTwofoldInstantiator.java new file mode 100644 index 0000000000..e9f6309c5b --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyTwofoldInstantiator.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import javax.enterprise.context.spi.CreationalContext; +import org.jboss.weld.injection.ConstructorInjectionPoint; +import org.jboss.weld.injection.producer.AbstractInstantiator; +import org.jboss.weld.manager.BeanManagerImpl; + +import java.lang.reflect.Constructor; + +/** + * An implementation of an instantiator capable of instantiating different instance for the client and server side. + * @param the class of the instantiator. + */ +public class JerseyTwofoldInstantiator extends AbstractInstantiator { + + private final AbstractInstantiator primaryInstantiator; + private ConstructorInjectionPoint optionalConstructorInjectionPoint = null; + + JerseyTwofoldInstantiator(AbstractInstantiator serverInstantiator) { + this.primaryInstantiator = serverInstantiator; + } + + @Override + public ConstructorInjectionPoint getConstructorInjectionPoint() { + return primaryInstantiator.getConstructorInjectionPoint(); + } + + @Override + public boolean hasInterceptorSupport() { + return primaryInstantiator.hasDecoratorSupport(); + } + + @Override + public boolean hasDecoratorSupport() { + return primaryInstantiator.hasDecoratorSupport(); + } + + @Override + public Constructor getConstructor() { + return primaryInstantiator.getConstructor(); + } + + @Override + public T newInstance(CreationalContext ctx, BeanManagerImpl manager) { + final ConstructorInjectionPoint cip = + optionalConstructorInjectionPoint == null || !JerseyClientCreationalContext.class.isInstance(ctx) + ? primaryInstantiator.getConstructorInjectionPoint() + : optionalConstructorInjectionPoint; + return cip.newInstance(manager, ctx); + } + + /** + * Set the optional constuctor injection point for the client side instantiation. + * @param optionalConstructorInjectionPoint The optional constructor injection point. + */ + public void setOptionalConstructorInjectionPoint(ConstructorInjectionPoint optionalConstructorInjectionPoint) { + this.optionalConstructorInjectionPoint = optionalConstructorInjectionPoint; + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/MultiException.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/MultiException.java new file mode 100644 index 0000000000..2c7e31a11c --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/MultiException.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.LinkedList; +import java.util.List; + +/** + * This exception can contain multiple other exceptions. + * However, it will also have the causal chain of the + * first exception added to the list of exceptions. + * + * @author John Wells (john.wells at oracle.com) + */ +public class MultiException extends RuntimeException { + /** + * For serialization + */ + private static final long serialVersionUID = 2112432697858621044L; + private final Object lock = new byte[0]; // byte[0] is an arbitrary type that is Serializable + private final List throwables = new LinkedList<>(); + private boolean reportToErrorService = true; + + /** + * Creates an empty MultiException. + */ + public MultiException() { + } + + /** + * This list must have at least one element in it. + * The first element of the list will become the + * cause of this exception, and its message will become + * the message of this exception. + * + * @param ths A non-null, non-empty list of exceptions. + */ + public MultiException(List ths) { + super(ths.get(0).getMessage(), ths.get(0)); + + for (Throwable th : ths) { + if (th instanceof MultiException) { + MultiException me = (MultiException) th; + + throwables.addAll(me.throwables); + } else { + throwables.add(th); + } + } + } + + /** + * This allows for construction of a MultiException + * with one element in its list. + * + * @param th May not be null. + */ + public MultiException(Throwable th, boolean reportToErrorService) { + super(th.getMessage(), th); + + if (th instanceof MultiException) { + MultiException me = (MultiException) th; + + throwables.addAll(me.throwables); + } else { + throwables.add(th); + } + + this.reportToErrorService = reportToErrorService; + } + + /** + * This allows for construction of a MultiException + * with one element in its list. + * + * @param th May not be null. + */ + public MultiException(Throwable th) { + this(th, true); + } + + /** + * Gets all the errors associated with this MultiException. + * + * @return All the errors associated with this MultiException. Will + * not return null, but may return an empty object. + */ + public List getErrors() { + synchronized (lock) { + return new LinkedList<>(throwables); + } + } + + /** + * Adds an error to an existing exception. + * + * @param error The exception to add. + */ + public void addError(Throwable error) { + synchronized (lock) { + throwables.add(error); + } + } + + /** + * Gets the message associated with this exception. + */ + public String getMessage() { + List listCopy = getErrors(); + StringBuffer sb = new StringBuffer("A MultiException has " + listCopy.size() + " exceptions. They are:\n"); + + int lcv = 1; + for (Throwable th : listCopy) { + sb.append(lcv++ + ". " + th.getClass().getName() + ((th.getMessage() != null) ? ": " + th.getMessage() : "") + "\n"); + } + + return sb.toString(); + } + + /** + * Prints the stack trace of this exception to the given PrintStream. + */ + public void printStackTrace(PrintStream s) { + List listCopy = getErrors(); + + if (listCopy.size() <= 0) { + super.printStackTrace(s); + return; + } + + int lcv = 1; + for (Throwable th : listCopy) { + s.println("MultiException stack " + lcv++ + " of " + listCopy.size()); + th.printStackTrace(s); + } + } + + /** + * Prints the stack trace of this exception to the given PrintWriter. + */ + public void printStackTrace(PrintWriter s) { + List listCopy = getErrors(); + + if (listCopy.size() <= 0) { + super.printStackTrace(s); + return; + } + + int lcv = 1; + for (Throwable th : listCopy) { + s.println("MultiException stack " + lcv++ + " of " + listCopy.size()); + th.printStackTrace(s); + } + } + + /** + * Returns true if this exception should be reported + * to the error service when thrown during a creation + * or deletion of a service. + * + * @return true if this exception should be reported to + * the error service when creating or deleting a service. + */ + public boolean getReportToErrorService() { + return reportToErrorService; + } + + /** + * Sets if this exception should be reported + * to the error service when thrown during a creation + * or deletion of a service. + * + * @param report true if this exception should be reported to + * the error service when creating or deleting a service. + */ + public void setReportToErrorService(boolean report) { + reportToErrorService = report; + } + + @Override + public String toString() { + return getMessage(); + } + +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/NamedImpl.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/NamedImpl.java new file mode 100644 index 0000000000..6a7d5fb3c2 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/NamedImpl.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import javax.inject.Named; + +import org.glassfish.jersey.internal.inject.AnnotationLiteral; + +/** + * This is an implementation of the {@link Named} annotation. + * + * @author John Wells (john.wells at oracle.com) + */ +class NamedImpl extends AnnotationLiteral implements Named { + + /** + * For serialization + */ + private static final long serialVersionUID = 9110325112008963155L; + + private final String name; + + /** + * Creates a {@link Named} annotation with the given name. + * + * @param name The non-null name to give the annotation. + */ + NamedImpl(String name) { + this.name = name; + } + + @Override + public String value() { + return name; + } + + @Override + public String toString() { + return "@Named(" + name + ")"; + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/ReflectionUtils.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/ReflectionUtils.java new file mode 100644 index 0000000000..e47a9986bc --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/ReflectionUtils.java @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + +import org.glassfish.jersey.inject.weld.internal.type.GenericArrayTypeImpl; +import org.glassfish.jersey.inject.weld.internal.type.ParameterizedTypeImpl; +import org.glassfish.jersey.internal.util.collection.ImmutableCollectors; + +/** + * Utility class for getting the information from class using Reflection API. + * + * @author John Wells (john.wells at oracle.com) + * @author Petr Bouda + */ +class ReflectionUtils { + + private static final Logger LOGGER = Logger.getLogger(ReflectionUtils.class.getName()); + + static Set getAllFields(Class clazz) { + if (clazz == null || Object.class.equals(clazz)) { + return Collections.emptySet(); + } + if (clazz.isInterface()) { + return Collections.emptySet(); + } + + Set retVal = new LinkedHashSet<>(); + retVal.addAll(getDeclaredField(clazz)); + retVal.addAll(getAllFields(clazz.getSuperclass())); + return retVal; + } + + /** + * Gets the EXACT set of fields on this class only. No subclasses. So this set should be considered RAW and has not taken into + * account any subclasses. + * + * @param clazz The class to examine. + * @return all declared fields. + */ + private static Set getDeclaredField(final Class clazz) { + return Arrays.stream(secureGetDeclaredFields(clazz)).collect(ImmutableCollectors.toImmutableLinkedSet()); + } + + private static Field[] secureGetDeclaredFields(final Class clazz) { + return AccessController.doPrivileged((PrivilegedAction) clazz::getDeclaredFields); + } + + /** + * Resolves the generic type of a field given the actual class being instantiated + * + * @param topclass The instantiation class. Must not be null + * @param field The non-null field whose type to resolve + * @return The resolved field type by way of its subclasses. Will not return + * null, but may return the original fields generic type + */ + static Type resolveField(Class topclass, Field field) { + return resolveMember(topclass, field.getGenericType(), field.getDeclaringClass()); + } + + /** + * Resolves the generic type of a type and declaring class given the actual class being instantiated + * + * @param topclass The instantiation class. Must not be null + * @param lookingForType The type to resolve. Must not be null + * @param declaringClass The class of the entity declaring the lookingForType. Must not be null + * @return The resolved type by way of its subclasses. Will not return null but may + * return lookingForType if it could not be further resolved + */ + private static Type resolveMember(Class topclass, Type lookingForType, Class declaringClass) { + Map typeArguments = typesFromSubClassToDeclaringClass(topclass, declaringClass); + if (typeArguments == null) { + return lookingForType; + } + + if (lookingForType instanceof ParameterizedType) { + return fixTypeVariables((ParameterizedType) lookingForType, typeArguments); + } + + if (lookingForType instanceof GenericArrayType) { + return fixGenericArrayTypeVariables((GenericArrayType) lookingForType, typeArguments); + } + + if (!(lookingForType instanceof TypeVariable)) { + return lookingForType; + } + + TypeVariable tv = (TypeVariable) lookingForType; + String typeVariableName = tv.getName(); + + Type retVal = typeArguments.get(typeVariableName); + if (retVal == null) { + return lookingForType; + } + + if (retVal instanceof Class) { + return retVal; + } + + if (retVal instanceof ParameterizedType) { + return fixTypeVariables((ParameterizedType) retVal, typeArguments); + } + + if (retVal instanceof GenericArrayType) { + return fixGenericArrayTypeVariables((GenericArrayType) retVal, typeArguments); + } + + return retVal; + } + + private static Map typesFromSubClassToDeclaringClass(Class topClass, Class declaringClass) { + if (topClass.equals(declaringClass)) { + return null; + } + + Type superType = topClass.getGenericSuperclass(); + Class superClass = getRawClass(superType); + + while (superType != null && superClass != null) { + if (!(superType instanceof ParameterizedType)) { + // superType MUST be a Class in this case + if (superClass.equals(declaringClass)) { + return null; + } + + superType = superClass.getGenericSuperclass(); + superClass = getRawClass(superType); + + continue; + } + + ParameterizedType superPT = (ParameterizedType) superType; + + Map typeArguments = getTypeArguments(superClass, superPT); + + if (superClass.equals(declaringClass)) { + return typeArguments; + } + + superType = superClass.getGenericSuperclass(); + superClass = getRawClass(superType); + + if (superType instanceof ParameterizedType) { + superType = fixTypeVariables((ParameterizedType) superType, typeArguments); + } + } + + throw new AssertionError(topClass.getName() + " is not the same as or a subclass of " + declaringClass.getName()); + } + + /** + * Gets a mapping of type variable names of the raw class to type arguments of the parameterized type. + */ + private static Map getTypeArguments(Class rawClass, ParameterizedType type) { + Map typeMap = new HashMap<>(); + Type[] typeArguments = type.getActualTypeArguments(); + + int i = 0; + for (TypeVariable typeVariable : rawClass.getTypeParameters()) { + typeMap.put(typeVariable.getName(), typeArguments[i++]); + } + return typeMap; + } + + /** + * Replace any TypeVariables in the given type's arguments with + * the actual argument types. Return the given type if no replacing + * is required. + */ + private static Type fixTypeVariables(ParameterizedType type, Map typeArgumentsMap) { + Type[] newTypeArguments = getNewTypeArguments(type, typeArgumentsMap); + if (newTypeArguments != null) { + return new ParameterizedTypeImpl(type.getRawType(), newTypeArguments); + } + return type; + } + + /** + * Get a new array of type arguments for the given ParameterizedType, replacing any TypeVariables with + * actual types. The types should be found in the given arguments map, keyed by variable name. Return + * null if no arguments needed to be replaced. + */ + private static Type[] getNewTypeArguments(final ParameterizedType type, + final Map typeArgumentsMap) { + + Type[] typeArguments = type.getActualTypeArguments(); + Type[] newTypeArguments = new Type[typeArguments.length]; + boolean newArgsNeeded = false; + + int i = 0; + for (Type argType : typeArguments) { + if (argType instanceof TypeVariable) { + newTypeArguments[i] = typeArgumentsMap.get(((TypeVariable) argType).getName()); + newArgsNeeded = true; + } else if (argType instanceof ParameterizedType) { + ParameterizedType original = (ParameterizedType) argType; + + Type[] internalTypeArgs = getNewTypeArguments(original, typeArgumentsMap); + if (internalTypeArgs != null) { + newTypeArguments[i] = new ParameterizedTypeImpl(original.getRawType(), internalTypeArgs); + newArgsNeeded = true; + } else { + newTypeArguments[i] = argType; + } + } else if (argType instanceof GenericArrayType) { + GenericArrayType gat = (GenericArrayType) argType; + + Type internalTypeArg = getNewTypeArrayArguments(gat, typeArgumentsMap); + if (internalTypeArg != null) { + if (internalTypeArg instanceof Class) { + newTypeArguments[i] = getArrayOfType((Class) internalTypeArg); + newArgsNeeded = true; + } else if ((internalTypeArg instanceof ParameterizedType) + && (((ParameterizedType) internalTypeArg).getRawType() instanceof Class)) { + ParameterizedType pt = (ParameterizedType) internalTypeArg; + + newTypeArguments[i] = getArrayOfType((Class) pt.getRawType()); + newArgsNeeded = true; + } else { + newTypeArguments[i] = new GenericArrayTypeImpl(internalTypeArg); + newArgsNeeded = true; + } + } else { + newTypeArguments[i] = argType; + } + } else { + newTypeArguments[i] = argType; + } + + i++; + } + + return newArgsNeeded ? newTypeArguments : null; + } + + /** + * Get a new Type for a GenericArrayType, replacing any TypeVariables with + * actual types. The types should be found in the given arguments map, keyed by variable name. Return + * null if no arguments needed to be replaced. + */ + private static Type getNewTypeArrayArguments(final GenericArrayType gat, + final Map typeArgumentsMap) { + + Type typeArgument = gat.getGenericComponentType(); + + if (typeArgument instanceof TypeVariable) { + return typeArgumentsMap.get(((TypeVariable) typeArgument).getName()); + } + + if (typeArgument instanceof ParameterizedType) { + ParameterizedType original = (ParameterizedType) typeArgument; + + Type[] internalTypeArgs = getNewTypeArguments(original, typeArgumentsMap); + if (internalTypeArgs != null) { + return new ParameterizedTypeImpl(original.getRawType(), internalTypeArgs); + } + + return original; + } + + if (typeArgument instanceof GenericArrayType) { + GenericArrayType original = (GenericArrayType) typeArgument; + + Type internalTypeArg = getNewTypeArrayArguments(original, typeArgumentsMap); + if (internalTypeArg != null) { + if (internalTypeArg instanceof Class) { + return getArrayOfType((Class) internalTypeArg); + } + + if (internalTypeArg instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) internalTypeArg; + + if (pt.getRawType() instanceof Class) { + return getArrayOfType((Class) pt.getRawType()); + } + } + + return new GenericArrayTypeImpl(internalTypeArg); + } + + return null; + } + + return null; + } + + /** + * Replace any TypeVariables in the given type's arguments with + * the actual argument types. Return the given type if no replacing + * is required. + */ + private static Type fixGenericArrayTypeVariables(GenericArrayType type, Map typeArgumentsMap) { + Type newTypeArgument = getNewTypeArrayArguments(type, typeArgumentsMap); + + if (newTypeArgument != null) { + if (newTypeArgument instanceof Class) { + return getArrayOfType((Class) newTypeArgument); + } + + if (newTypeArgument instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) newTypeArgument; + if (pt.getRawType() instanceof Class) { + return getArrayOfType((Class) pt.getRawType()); + } + } + + return new GenericArrayTypeImpl(newTypeArgument); + } + + return type; + } + + private static Class getArrayOfType(Class type) { + return Array.newInstance(type, 0).getClass(); + } + + /** + * Given the type parameter gets the raw type represented + * by the type, or null if this has no associated raw class + * + * @param type The type to find the raw class on + * @return The raw class associated with this type + */ + private static Class getRawClass(Type type) { + if (type == null) { + return null; + } + + if (type instanceof GenericArrayType) { + Type componentType = ((GenericArrayType) type).getGenericComponentType(); + + if (!(componentType instanceof ParameterizedType) && !(componentType instanceof Class)) { + // type variable is not supported + return null; + } + + Class rawComponentClass = getRawClass(componentType); + + String forNameName = "[L" + rawComponentClass.getName() + ";"; + try { + return Class.forName(forNameName); + } catch (Throwable th) { + // ignore, but return null + return null; + } + } + + if (type instanceof Class) { + return (Class) type; + } + + if (type instanceof ParameterizedType) { + Type rawType = ((ParameterizedType) type).getRawType(); + if (rawType instanceof Class) { + return (Class) rawType; + } + } + + return null; + } + + /** + * Sets the given field to the given value + * + * @param field The non-null field to set + * @param instance The non-null instance to set into + * @param value The value to which the field should be set + * @throws Throwable If there was some exception while setting the field + */ + static void setField(Field field, Object instance, Object value) throws Throwable { + setAccessible(field); + + try { + field.set(instance, value); + } catch (IllegalArgumentException | IllegalAccessException ex) { + LOGGER.warning(String.format("Failed during setting a value into Class: %s, Field: %s", + field.getDeclaringClass().getName(), field.getName())); + throw ex; + } + } + + /** + * Sets this accessible object to be accessible using the permissions of + * the hk2-locator bundle (which will need the required grant) + * + * @param ao The object to change + */ + private static void setAccessible(final AccessibleObject ao) { + if (ao.isAccessible()) { + return; + } + + AccessController.doPrivileged((PrivilegedAction) () -> { + ao.setAccessible(true); + return null; + }); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/WrappingJerseyInjectionTarget.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/WrappingJerseyInjectionTarget.java new file mode 100644 index 0000000000..48a3ded960 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/injector/WrappingJerseyInjectionTarget.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.InjectionException; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.inject.spi.InjectionTarget; + +import org.glassfish.jersey.internal.inject.InjectionResolver; +import org.glassfish.jersey.internal.util.collection.LazyValue; +import org.glassfish.jersey.internal.util.collection.Value; +import org.glassfish.jersey.internal.util.collection.Values; + +/** + * An implementation of {@link InjectionTarget} that just wraps the provided {@code InjectionTarget} because of additional + * features in an injection phase. + * + * @author Petr Bouda + */ +public class WrappingJerseyInjectionTarget extends AbstractInjectionTarget { + + private static InjectionTarget NOOP_INJECTION_TARGET = new NoOpInjectionTarget(); + + private final Bean bean; + private final LazyValue> injector; + private final InjectionTarget delegate; + private Collection resolvers; + + /** + * Creates a new jersey injection target with delegate as a {@link NoOpInjectionTarget} that creates no operation that + * means that only jersey injection is available as a additional feature. + * + * @param bean bean as descriptor of the class which will be injected. + * @param resolvers all resolvers that can provide a valued for Jersey-specific injection. + */ + public WrappingJerseyInjectionTarget(Bean bean, Collection resolvers) { + this(NOOP_INJECTION_TARGET, bean, resolvers); + } + + /** + * An implementation of {@link InjectionTarget} for classes that do not fulfill bean class requirements + * (e.g. are abstract or non-static inner classes). Instances of these class can be injected using this implementation. If the + * application attempts to {@link #produce(CreationalContext)} a new instance of the class, {@code CreationException} is + * thrown. + * + * @param delegate CDI specific injection target. + * @param bean bean as descriptor of the class which will be injected. + * @param resolvers all resolvers that can provide a valued for Jersey-specific injection. + */ + public WrappingJerseyInjectionTarget(InjectionTarget delegate, Bean bean, Collection resolvers) { + this.bean = bean; + this.delegate = delegate; + this.resolvers = resolvers; + this.injector = Values.lazy((Value>) + () -> new JerseyInstanceInjector<>(bean, this.resolvers)); + } + + @Override + public void inject(T instance, CreationalContext ctx) { + /* + * If an instance contains any fields which be injected by Jersey then Jersey attempts to inject them using annotations + * retrieves from registered InjectionResolvers. + */ + try { + injector.get().inject(instance); + } catch (Throwable cause) { + throw new InjectionException( + "Exception occurred during Jersey/JAX-RS annotations processing in the class: " + bean.getBeanClass(), cause); + } + + /* + * The rest of the fields (annotated by @Inject) are injected using CDI. + */ + super.inject(instance, ctx); + } + + @Override + InjectionTarget delegate() { + return delegate; + } + + private static class NoOpInjectionTarget implements InjectionTarget { + + @Override + public void inject(Object instance, CreationalContext ctx) { + } + + @Override + public void postConstruct(Object instance) { + } + + @Override + public void preDestroy(Object instance) { + } + + @Override + public Object produce(CreationalContext ctx) { + return null; + } + + @Override + public void dispose(Object instance) { + } + + @Override + public Set getInjectionPoints() { + return Collections.emptySet(); + } + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java new file mode 100644 index 0000000000..da291dfd43 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/BinderRegisterExtension.java @@ -0,0 +1,912 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import javax.annotation.Priority; +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.Dependent; +import javax.enterprise.context.NormalScope; +import javax.enterprise.context.SessionScoped; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.BeforeBeanDiscovery; +import javax.enterprise.inject.spi.ProcessAnnotatedType; +import javax.enterprise.inject.spi.Unmanaged; +import javax.enterprise.inject.spi.WithAnnotations; +import javax.inject.Scope; +import javax.inject.Singleton; + +import javax.enterprise.event.Observes; +import javax.enterprise.inject.spi.AfterBeanDiscovery; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.Extension; +import javax.enterprise.inject.spi.ProcessInjectionTarget; + +import javax.ws.rs.Path; +import javax.ws.rs.RuntimeType; +import javax.ws.rs.container.DynamicFeature; +import javax.ws.rs.core.Feature; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.Application; +import javax.ws.rs.ext.Provider; + +import org.glassfish.jersey.inject.weld.internal.data.BindingBeanPair; +import org.glassfish.jersey.inject.weld.internal.inject.InitializableSupplierInstanceBinding; +import org.glassfish.jersey.inject.weld.internal.scope.RequestScopeBean; +import org.glassfish.jersey.inject.weld.internal.inject.InitializableInstanceBinding; +import org.glassfish.jersey.inject.weld.internal.bean.BeanHelper; +import org.glassfish.jersey.inject.weld.internal.injector.ContextInjectionResolverImpl; +import org.glassfish.jersey.inject.weld.internal.injector.JerseyInjectionTarget; +import org.glassfish.jersey.inject.weld.internal.scope.CdiRequestScope; +import org.glassfish.jersey.inject.weld.spi.BootstrapPreinitialization; +import org.glassfish.jersey.internal.BootstrapBag; +import org.glassfish.jersey.internal.ServiceFinder; +import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.Binder; +import org.glassfish.jersey.internal.inject.Binding; +import org.glassfish.jersey.internal.inject.Bindings; +import org.glassfish.jersey.internal.inject.ClassBinding; +import org.glassfish.jersey.internal.inject.CustomAnnotationLiteral; +import org.glassfish.jersey.internal.inject.ForeignDescriptor; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.InjectionResolver; +import org.glassfish.jersey.internal.inject.InjectionResolverBinding; +import org.glassfish.jersey.internal.inject.InstanceBinding; +import org.glassfish.jersey.internal.inject.Providers; +import org.glassfish.jersey.internal.inject.ServiceHolder; +import org.glassfish.jersey.internal.inject.SupplierClassBinding; +import org.glassfish.jersey.internal.inject.SupplierInstanceBinding; + +import org.glassfish.jersey.internal.util.collection.Ref; +import org.glassfish.jersey.internal.util.collection.Refs; +import org.glassfish.jersey.process.internal.RequestScope; +import org.glassfish.jersey.process.internal.RequestScoped; +import org.glassfish.jersey.server.ApplicationHandler; +import org.glassfish.jersey.server.ResourceConfig; +import org.jboss.weld.injection.producer.BasicInjectionTarget; +import org.jboss.weld.injection.producer.BeanInjectionTarget; + +/** + * CDI extension that handles CDI bootstrap events and registers Jersey's internally used components and components registered + * using {@link Application}. + */ +class BinderRegisterExtension implements Extension { + + private AtomicBoolean registrationDone = new AtomicBoolean(false); + private Supplier beanManagerSupplier; + private Ref serverInjectionManager = Refs.emptyRef(); + + private BootstrapInjectionManager clientBootstrapInjectionManager = new BootstrapInjectionManager(RuntimeType.CLIENT); + private WrappingInjectionManager serverBootstrapInjectionManager = new WrappingInjectionManager() + .setInjectionManager(new BootstrapInjectionManager(RuntimeType.SERVER)); + private BootstrapBag bootstrapBag = new BootstrapBag(); + + private final CachingBinder clientBindings = new CachingBinder(serverInjectionManager); + private final CachingBinder serverBindings = new CachingBinder(serverInjectionManager) { + @Override + protected void configure() { + install(new ContextInjectionResolverImpl.Binder(beanManagerSupplier)); + bind(InitializableInstanceBinding.from(Bindings.service(serverInjectionManager.get()).to(InjectionManager.class))); + } + }; + private final CachingBinder annotatedBeansBinder = new CachingBinder(serverInjectionManager); + private final MergedBindings mergedBindings = new MergedBindings(serverBindings, clientBindings); + + private final List initializableInstanceBindings = new LinkedList<>(); + private final List initializableSupplierInstanceBindings = new LinkedList<>(); + private final MultivaluedMap supplierClassBindings = new MultivaluedHashMap<>(); + private final MultivaluedMap classBindings = new MultivaluedHashMap<>(); + private final List jerseyInjectionTargets = new LinkedList<>(); + private final List injectionResolvers = new LinkedList<>(); + private final Map, Class> annotatedBeans = new HashMap<>(); + private final List> applications = new LinkedList<>(); + final Set> managedBeans = new HashSet<>(); + + /** + * Ignores the classes which are manually added using bindings (through {@link Application} class) and scanned by CDI. + * The manual adding is privileged and the beans scanned using CDI are ignored. + *

          + * TODO: The method counts with the condition that the all bindings are known before the CDI scanning has been started, + * can be changed during the migration from CDI SE to JAVA EE environment. + * + * @param pat processed type. + * @param type of the scanned bean. + */ + void ignoreManuallyRegisteredComponents( + @Observes @WithAnnotations({ Path.class, Provider.class }) ProcessAnnotatedType pat) { + final AnnotatedType annotatedType = pat.getAnnotatedType(); + for (Binding binding : mergedBindings.getBindings()) { + if (ClassBinding.class.isAssignableFrom(binding.getClass())) { + ClassBinding classBinding = (ClassBinding) binding; + if (annotatedType.getJavaClass() == classBinding.getService()) { + pat.veto(); + return; + } + } else if (InstanceBinding.class.isAssignableFrom(binding.getClass())) { + InstanceBinding instanceBinding = (InstanceBinding) binding; + if (annotatedType.getJavaClass() == instanceBinding.getService().getClass()) { + pat.veto(); + return; + } + } + } + if (annotatedType.isAnnotationPresent(Path.class)) { + boolean hasScope = false; + for (Annotation annotation : annotatedType.getAnnotations()) { + if (annotation.annotationType().isAnnotationPresent(Scope.class) + || annotation.annotationType().isAnnotationPresent(NormalScope.class)) { + hasScope = true; + break; + } + } + if (!hasScope) { + annotatedBeans.put(annotatedType.getJavaClass(), javax.enterprise.context.RequestScoped.class); + pat.configureAnnotatedType().add(javax.enterprise.context.RequestScoped.Literal.INSTANCE); + } + } + + + } + + void registerJerseyRequestScopedResources( + @Observes @WithAnnotations(RequestScoped.class) ProcessAnnotatedType pat) { + if (pat.getAnnotatedType().isAnnotationPresent(RequestScoped.class) + && !pat.getAnnotatedType().isAnnotationPresent(javax.enterprise.context.RequestScoped.class)) { + pat.configureAnnotatedType().remove(a -> RequestScoped.class.isInstance(a)) + .add(javax.enterprise.context.RequestScoped.Literal.INSTANCE); + annotatedBeans.put(pat.getAnnotatedType().getJavaClass(), javax.enterprise.context.RequestScoped.class); + } + } + + void processRegistrars(@Observes BeforeBeanDiscovery beforeBeanDiscovery, BeanManager beanManager) { + CdiInjectionManagerFactoryBase.setBeanManager(beanManager); + processRegistrars(); + } + + void handleRequestScoped( + @Observes @WithAnnotations({javax.enterprise.context.RequestScoped.class}) ProcessAnnotatedType pat) { + final Class javaClass = pat.getAnnotatedType().getJavaClass(); + if (isJaxrs(javaClass) && isNotJerseyInternal(javaClass)) { + pat.configureAnnotatedType().add(CustomAnnotationLiteral.INSTANCE); + annotatedBeans.put(javaClass, javax.enterprise.context.RequestScoped.class); + } + } + + void handleApplicationScoped( + @Observes @WithAnnotations({ApplicationScoped.class}) ProcessAnnotatedType pat) { + final Class javaClass = pat.getAnnotatedType().getJavaClass(); + if (Application.class.isAssignableFrom(javaClass)) { + pat.veto(); + applications.add((Class) javaClass); + } else if (isJaxrs(javaClass) && isNotJerseyInternal(javaClass)) { + pat.configureAnnotatedType().add(CustomAnnotationLiteral.INSTANCE); + annotatedBeans.put(javaClass, ApplicationScoped.class); + } + } + + void handleDependent(@Observes @WithAnnotations({Dependent.class}) ProcessAnnotatedType pat) { + final Class javaClass = pat.getAnnotatedType().getJavaClass(); + if (isJaxrs(javaClass) && isNotJerseyInternal(javaClass)) { + pat.configureAnnotatedType().add(CustomAnnotationLiteral.INSTANCE); + annotatedBeans.put(javaClass, Dependent.class); + } + } + + void handleSessionScoped(@Observes @WithAnnotations({SessionScoped.class}) ProcessAnnotatedType pat) { + final Class javaClass = pat.getAnnotatedType().getJavaClass(); + if (isJaxrs(javaClass) && isNotJerseyInternal(javaClass)) { + pat.configureAnnotatedType().add(CustomAnnotationLiteral.INSTANCE); + annotatedBeans.put(javaClass, SessionScoped.class); + } + } + + void registerSingletonSubResources(@Observes @WithAnnotations({Singleton.class}) ProcessAnnotatedType pat){ + final Class resourceClass = pat.getAnnotatedType().getJavaClass(); + if (resourceClass.getAnnotation(Path.class) != null) { + annotatedBeans.put(resourceClass, Singleton.class); + } else if (BeanHelper.isResourceClass(resourceClass)) { + annotatedBeans.put(resourceClass, Singleton.class); + } + } + + void registerJerseyProviders(@Observes @WithAnnotations({Priority.class}) ProcessAnnotatedType pat) { + final Class javaClass = pat.getAnnotatedType().getJavaClass(); + if (!isNotJerseyInternal(javaClass)) { + pat.veto(); //veto Jersey internal + } + annotatedBeans.put(javaClass, Priority.class); + } + + /** + * Wraps all JAX-RS components by Jersey-specific injection target. + * + * @param pit process injection target. + * @param type of the processed injection target. + */ + public void observeInjectionTarget(@Observes ProcessInjectionTarget pit) { + if (!BeanInjectionTarget.class.isInstance(pit.getInjectionTarget())) { + return; + } + BasicInjectionTarget it = (BasicInjectionTarget) pit.getInjectionTarget(); + JerseyInjectionTarget jerseyInjectionTarget = + new JerseyInjectionTarget<>(it, pit.getAnnotatedType().getJavaClass()); + jerseyInjectionTargets.add(jerseyInjectionTarget); + pit.setInjectionTarget(jerseyInjectionTarget); + } + + /** + * Takes all registered bindings and registers them in {@link BeanManager}. + *

          + * Method should register only Jersey internal components and class/instances registered using {@link Application}. Registered + * classes/instances have priority therefore CDI scanning should veto these classes/instances during { + * + * @param abd {@code AfterBeanDiscovery} event. + * @param beanManager current {@code BeanManager}. + * @link ProcessAnnotatedType} bootstrap phase. + */ + void registerBeans(@Observes AfterBeanDiscovery abd, BeanManager beanManager) { + serverInjectionManager.set(new CdiInjectionManager(beanManager, mergedBindings)); + + beanManagerSupplier = () -> beanManager; // set bean manager supplier to be called by bindings#configure + CdiInjectionManagerFactoryBase.setBeanManager(beanManager); + + registerApplicationHandler(beanManager); + + registrationDone.set(true); // + + final List contextInjectionResolvers = serverBindings.getBindings().stream() + .filter(binding -> InjectionResolverBinding.class.isAssignableFrom(binding.getClass())) + .map(InjectionResolverBinding.class::cast) + .map(InjectionResolverBinding::getResolver) + .collect(Collectors.toList()); + + injectionResolvers.addAll(contextInjectionResolvers); + + /* + * Provide registered InjectionResolvers to Jersey's components which has been discovered by CDI in + * ProcessInjectionTarget bootstrap phase. + */ + jerseyInjectionTargets.forEach(injectionTarget -> injectionTarget.setInjectionResolvers(injectionResolvers)); + + registerBeans(RuntimeType.SERVER, this.serverBindings, abd, beanManager); + registerBeans(RuntimeType.CLIENT, this.clientBindings, abd, beanManager); + + abd.addBean(new RequestScopeBean(beanManager)); + + addAnnotatedBeans(abd, beanManager); + + serverBootstrapInjectionManager.setInjectionManager(serverInjectionManager.get()); + ((CdiInjectionManager) serverInjectionManager.get()).managedBeans = managedBeans; + } + + private void registerBeans(RuntimeType runtimeType, CachingBinder binder, AfterBeanDiscovery abd, + BeanManager beanManager) { + final Collection bindings = binder.getBindings(); + binder.setReadOnly(); + + allBindingsLabel: + for (Binding binding : bindings) { + if (ClassBinding.class.isAssignableFrom(binding.getClass())) { + if (RuntimeType.CLIENT == runtimeType) { + for (Type contract : ((ClassBinding) binding).getContracts()) { + final List preregistered = classBindings.get(contract); + if (preregistered != null && preregistered.size() == 1) { + BeanHelper.updateBean( + (ClassBinding) binding, preregistered.get(0), injectionResolvers, beanManager); + continue allBindingsLabel; + } + } + } + BindingBeanPair pair = BeanHelper.registerBean( + runtimeType, (ClassBinding) binding, abd, injectionResolvers, beanManager); + for (Type contract : ((ClassBinding) binding).getContracts()) { + classBindings.add(contract, pair); + } + } else if (SupplierClassBinding.class.isAssignableFrom(binding.getClass())) { + if (RuntimeType.CLIENT == runtimeType) { + for (Type contract : ((SupplierClassBinding) binding).getContracts()) { + final List preregistered = supplierClassBindings.get(contract); + if (preregistered != null && preregistered.size() == 1) { + BeanHelper.updateSupplierBean( + (SupplierClassBinding) binding, preregistered.get(0), injectionResolvers, beanManager); + continue allBindingsLabel; + } + } + } + BindingBeanPair pair = BeanHelper.registerSupplier( + runtimeType, (SupplierClassBinding) binding, abd, injectionResolvers, beanManager); + for (Type contract : ((SupplierClassBinding) binding).getContracts()) { + supplierClassBindings.add(contract, pair); + } + } else if (InitializableInstanceBinding.class.isAssignableFrom(binding.getClass())) { + if (RuntimeType.SERVER == runtimeType + || !matchInitializableInstanceBinding((InitializableInstanceBinding) binding)) { + initializableInstanceBindings.add((InitializableInstanceBinding) binding); + BeanHelper.registerBean( + runtimeType, (InitializableInstanceBinding) binding, abd, injectionResolvers, beanManager); + } + } else if (InitializableSupplierInstanceBinding.class.isInstance(binding)) { + if (RuntimeType.SERVER == runtimeType + || !matchInitializableSupplierInstanceBinding((InitializableSupplierInstanceBinding) binding)) { + initializableSupplierInstanceBindings.add((InitializableSupplierInstanceBinding) binding); + BeanHelper.registerSupplier(runtimeType, (InitializableSupplierInstanceBinding) binding, abd, beanManager); + } + } + } + } + + private void addAnnotatedBeans(AfterBeanDiscovery abd, BeanManager beanManager) { + for (Map.Entry, Class> contract : annotatedBeans.entrySet()) { + for (Binding binding : serverBindings.getBindings()) { + if (ClassBinding.class.isInstance(binding)) { + if (((ClassBinding) binding).getService() == contract.getClass()) { + break; + } + } + if (InitializableInstanceBinding.class.isInstance(binding)) { + if (((InitializableInstanceBinding) binding).getImplementationType() == contract.getClass()) { + break; + } + } + } + if (isNotJerseyInternal(contract.getKey())) { + if (beanManager.getBeans(contract.getKey()).isEmpty()) { + final ClassBinding binding = bind(contract.getKey(), annotatedBeansBinder); + if (Singleton.class.equals(contract.getValue())) { + binding.in(Singleton.class); + } + } + managedBeans.add(contract.getKey()); // add either way + } + } + + registerBeans(RuntimeType.SERVER, annotatedBeansBinder, abd, beanManager); + serverBindings.getBindings().addAll(annotatedBeansBinder.getBindings()); + } + + private void registerApplicationHandler(BeanManager beanManager) { + final ResourceConfig resourceConfig = new ResourceConfig(); + + for (Class applicationClass : applications) { + bindApplication(applicationClass, resourceConfig, beanManager); + } + + new ApplicationHandler(resourceConfig); + } + + private void bindApplication(Class applicationClass, ResourceConfig resourceConfig, BeanManager beanManager) { + final Application application = new Unmanaged<>(applicationClass).newInstance().produce().get(); + + for (Class clazz : application.getClasses()) { + if (beanManager.getBeans(clazz).isEmpty()) { + // prevent double registration of a class + // bind(clazz, binder); + resourceConfig.register(clazz); + } else if (!annotatedBeans.containsKey(clazz)) { + annotatedBeans.put(clazz, Provider.class); + } + } + for (Object singleton : application.getSingletons()) { + final Class clazz = singleton.getClass(); + if (beanManager.getBeans(clazz).isEmpty()) { + // prevent double registration of a class +// final InstanceBinding binding = binder.bind(singleton); +// toSuper(clazz, binding); + resourceConfig.register(singleton); + } else if (!annotatedBeans.containsKey(clazz)) { + annotatedBeans.put(clazz, Provider.class); + } + } + } + +// private void bindApplication(Class applicationClass, AbstractBinder binder, BeanManager beanManager) { +// final Application application = new Unmanaged<>(applicationClass).newInstance().produce().get(); +// final CommonConfig commonConfig = new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL); +// +// for (Class clazz : application.getClasses()) { +// if (beanManager.getBeans(clazz).isEmpty()) { +// // prevent double registration of a class +// // bind(clazz, binder); +// commonConfig.register(clazz); +// } else if (!annotatedBeans.containsKey(clazz)) { +// annotatedBeans.put(clazz, Provider.class); +// } +// } +// for (Object singleton : application.getSingletons()) { +// final Class clazz = singleton.getClass(); +// if (beanManager.getBeans(clazz).isEmpty()) { +// // prevent double registration of a class +//// final InstanceBinding binding = binder.bind(singleton); +//// toSuper(clazz, binding); +// commonConfig.register(singleton); +// } else if (!annotatedBeans.containsKey(clazz)) { +// annotatedBeans.put(clazz, Provider.class); +// } +// } +// } + + private static ClassBinding bind(Class clazz, AbstractBinder binder) { + final ClassBinding binding = binder.bindAsContract(clazz); + return toSuper(clazz, binding); + } + + private static T toSuper(Class clazz, T binding) { + Class superClass = clazz; + while (superClass != null) { + superClass = superClass.getSuperclass(); + if (superClass != null) { + binding.to(superClass); + } + } + for (Class intf : clazz.getInterfaces()){ + binding.to(intf); + } + return binding; + } + +// // Check first if a class is a JAX-RS resource, and only if so check with validation. +// // This prevents unnecessary warnings being logged for pure CDI beans. +// private final Cache, Boolean> jaxRsResourceCache = new Cache<>( +// clazz -> Resource.from(clazz, true) != null && Resource.from(clazz) != null); +// +// public boolean isJaxRsResource(Class resource) { +// return jaxRsResourceCache.apply(resource); +// } + + private boolean isJaxrs(Class clazz) { + return Providers.isJaxRsProvider(clazz) || BeanHelper.isResourceClass(clazz) || isJerseyRegistrable(clazz); + } + + private boolean isJerseyRegistrable(Class clazz) { + return Feature.class.isAssignableFrom(clazz) || DynamicFeature.class.isAssignableFrom(clazz); + } + + private boolean isNotJerseyInternal(Class clazz) { + final Package pkg = clazz.getPackage(); + if (pkg == null) { // Class.getPackage() could return null + return false; + } + + final String pkgName = pkg.getName(); + return !pkgName.startsWith("org.glassfish.jersey") + || pkgName.startsWith("org.glassfish.jersey.examples") + || pkgName.startsWith("org.glassfish.jersey.tests"); + } + + private boolean matchInitializableInstanceBinding(InitializableInstanceBinding candidate) { + for (InitializableInstanceBinding iib : initializableInstanceBindings) { + if (iib.matches(candidate).matches()) { + return true; + } + } + return false; + } + + private boolean matchInitializableSupplierInstanceBinding(InitializableSupplierInstanceBinding candidate) { + for (InitializableSupplierInstanceBinding isib : initializableSupplierInstanceBindings) { + if (isib.matches(candidate).matches()) { + return true; + } + } + return false; + } + + + /** To be used by the tests only */ + public void register(BeforeBeanDiscovery beforeBeanDiscovery, Binding binding) { + register(RuntimeType.SERVER, binding); + } + + /** To be used by the tests only */ + public void register(BeforeBeanDiscovery beforeBeanDiscovery, Iterable bindings) { + register(RuntimeType.SERVER, bindings); + } + + private void register(RuntimeType runtimeType, Binding binding) { + final AbstractBinder bindings = runtimeType == RuntimeType.CLIENT ? clientBindings : serverBindings; + if (InstanceBinding.class.isInstance(binding)) { + bindings.bind(InitializableInstanceBinding.from((InstanceBinding) binding)); + } else if (SupplierInstanceBinding.class.isInstance(binding)) { + bindings.bind(InitializableSupplierInstanceBinding.from((SupplierInstanceBinding) binding)); + } else { + bindings.bind(binding); + } + } + + private void register(RuntimeType runtimeType, Iterable bindings) { + for (Binding binding : bindings) { + register(runtimeType, binding); + } + } + + private void processRegistrars() { + final List registrars = new LinkedList<>(); + for (BootstrapPreinitialization registrar : ServiceFinder.find(BootstrapPreinitialization.class)) { + registrars.add(registrar); + } + for (BootstrapPreinitialization registrar : registrars) { + registrar.register(RuntimeType.SERVER, serverBindings); + } + + for (BootstrapPreinitialization registrar : registrars) { + registrar.register(RuntimeType.CLIENT, clientBindings); + } + } + + InjectionManager getInjectionManager(RuntimeType runtimeType) { + if (RuntimeType.CLIENT == runtimeType) { + return registrationDone.get() + ? new CdiClientInjectionManager(beanManagerSupplier.get(), mergedBindings) + : clientBootstrapInjectionManager; + } else { + return registrationDone.get() ? serverInjectionManager.get() : serverBootstrapInjectionManager; + } + } + + /** + * Injection manager used during the bootstrap. It is used to create the actual beans in the beans manager. + * Other InjectionManagers sets the beans (beans binding) by a value provided in runtime. + */ + private class BootstrapInjectionManager implements InjectionManager { + + private final RuntimeType runtimeType; + + private BootstrapInjectionManager(RuntimeType runtimeType) { + this.runtimeType = runtimeType; + } + + @Override + public void completeRegistration() { + //noop + } + + @Override + public void shutdown() { + //noop + } + + @Override + public void register(Binding binding) { + BinderRegisterExtension.this.register(runtimeType, binding); + } + + @Override + public void register(Iterable descriptors) { + for (Binding binding : descriptors) { + register(binding); + } + } + + @Override + public void register(Binder binder) { + register(binder.getBindings()); + } + + @Override + public void register(Object provider) throws IllegalArgumentException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isRegistrable(Class clazz) { + return false; + } + + @Override + public T createAndInitialize(Class createMe) { + if (RequestScope.class == createMe) { + return (T) new CdiRequestScope(); + } + if (isNotJerseyInternal(createMe)) { + return null; + } + try { + Constructor constructor = createMe.getConstructor(); + return constructor.newInstance(); + } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) { + return null; + } + } + + @Override + public T create(Class createMe) { + return createAndInitialize(createMe); + } + + @Override + public List> getAllServiceHolders(Class contractOrImpl, Annotation... qualifiers) { + return Collections.EMPTY_LIST; + } + + @Override + public T getInstance(Class contractOrImpl, Annotation... qualifiers) { + return getInstance(contractOrImpl); + } + + @Override + public T getInstance(Class contractOrImpl, String classAnalyzer) { + return getInstance(contractOrImpl); + } + + @Override + public T getInstance(Class contractOrImpl) { + return createAndInitialize(contractOrImpl); + } + + @Override + public T getInstance(Type contractOrImpl) { + return (T) createAndInitialize((Class) contractOrImpl); + } + + @Override + public Object getInstance(ForeignDescriptor foreignDescriptor) { + throw new UnsupportedOperationException(); + } + + @Override + public ForeignDescriptor createForeignDescriptor(Binding binding) { + throw new UnsupportedOperationException(); + } + + @Override + public List getAllInstances(Type contractOrImpl) { + final T t = getInstance(contractOrImpl); + return t != null ? Collections.singletonList(t) : Collections.EMPTY_LIST; + } + + @Override + public void inject(Object injectMe) { + // noop; + } + + @Override + public void inject(Object injectMe, String classAnalyzer) { + throw new UnsupportedOperationException(); + } + + @Override + public void preDestroy(Object preDestroyMe) { + //noop + } + } + + /** + * AbstractBinder that supports calling {@link #getBindings()} multiple times by caching the result. + * Each additional binding is added to the cache by the next call of {@link #getBindings()}. + * When {@link #setReadOnly()} is called, no additional binding is added to the cache. + */ + private class CachingBinder extends AbstractBinder { + private final Ref injectionManager; + private AbstractBinder temporaryBinder = new TemporaryBinder(); + private final Collection bindings = new LinkedList<>(); + + private CachingBinder(Ref injectionManager) { + this.injectionManager = injectionManager; + } + + @Override + protected void configure() { + // noop + } + + @Override + public ClassBinding bind(Class serviceType) { + return temporaryBinder.bind(serviceType); + } + + @Override + public Binding bind(Binding binding) { + return temporaryBinder.bind(binding); + } + + @Override + public ClassBinding bindAsContract(GenericType serviceType) { + return temporaryBinder.bindAsContract(serviceType); + } + + @Override + public ClassBinding bindAsContract(Class serviceType) { + return temporaryBinder.bindAsContract(serviceType); + } + + @Override + public ClassBinding bindAsContract(Type serviceType) { + return temporaryBinder.bindAsContract(serviceType); + } + + @Override + public InstanceBinding bind(T service) { + return temporaryBinder.bind(service); + } + + @Override + public SupplierClassBinding bindFactory( + Class> supplierType, Class supplierScope) { + return temporaryBinder.bindFactory(supplierType, supplierScope); + } + + @Override + public SupplierClassBinding bindFactory(Class> supplierType) { + return temporaryBinder.bindFactory(supplierType); + } + + @Override + public SupplierInstanceBinding bindFactory(Supplier factory) { + return temporaryBinder.bindFactory(factory); + } + + @Override + public InjectionResolverBinding bind(T resolver) { + return temporaryBinder.bind(resolver); + } + + @Override + public Collection getBindings() { + if (!readOnly) { + if (registrationDone.get()) { + bindings.addAll(super.getBindings()); + } + final Collection newBindings = temporaryBinder.getBindings(); + for (Binding binding : newBindings) { + if (InstanceBinding.class.isAssignableFrom(binding.getClass())) { + binding = InitializableInstanceBinding.from((InstanceBinding) binding); + } else if (SupplierInstanceBinding.class.isAssignableFrom(binding.getClass())) { + binding = InitializableSupplierInstanceBinding.from((SupplierInstanceBinding) binding); + } + bindings.add(binding); + } + temporaryBinder = new TemporaryBinder(); + } + return bindings; + } + + private boolean readOnly = false; + + void setReadOnly() { + readOnly = true; + } + + private class TemporaryBinder extends AbstractBinder { + + @Override + protected void configure() { + // do nothing + } + } + } + + private static class MergedBindings implements Binder { + private final AbstractBinder first; + private final AbstractBinder second; + + + private MergedBindings(AbstractBinder first, AbstractBinder second) { + this.first = first; + this.second = second; + } + + public Collection getBindings() { + final Collection firstBindings = first.getBindings(); + final Collection secondBindings = second.getBindings(); + + Collection merged = new Collection() { + @Override + public int size() { + return firstBindings.size() + secondBindings.size(); + } + + @Override + public boolean isEmpty() { + return firstBindings.isEmpty() && secondBindings.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return firstBindings.contains(o) || secondBindings.contains(o); + } + + @Override + public Iterator iterator() { + final Iterator firstIterator = firstBindings.iterator(); + final Iterator secondIterator = secondBindings.iterator(); + return new Iterator() { + @Override + public boolean hasNext() { + return firstIterator.hasNext() || secondIterator.hasNext(); + } + + @Override + public Binding next() { + return firstIterator.hasNext() ? firstIterator.next() : secondIterator.next(); + } + }; + } + + // Used by IDE while debugging + @Override + public Object[] toArray() { + Object[] array = new Object[size()]; + final Iterator bindingIterator = iterator(); + int i = 0; + while (bindingIterator.hasNext()) { + array[i++] = bindingIterator.next(); + } + return array; + } + + @Override + public T[] toArray(T[] a) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean add(Binding binding) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + }; + return merged; + } + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/CdiClientInjectionManager.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/CdiClientInjectionManager.java new file mode 100644 index 0000000000..46db64b289 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/CdiClientInjectionManager.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.inject.weld.internal.bean.JerseyBean; +import org.glassfish.jersey.inject.weld.internal.inject.InitializableInstanceBinding; +import org.glassfish.jersey.inject.weld.internal.inject.InitializableSupplierInstanceBinding; +import org.glassfish.jersey.inject.weld.internal.inject.MatchableBinding; +import org.glassfish.jersey.inject.weld.internal.injector.JerseyClientCreationalContext; +import org.glassfish.jersey.internal.inject.Binder; +import org.glassfish.jersey.internal.inject.Binding; +import org.glassfish.jersey.internal.inject.Bindings; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.InstanceBinding; +import org.glassfish.jersey.internal.inject.SupplierClassBinding; +import org.glassfish.jersey.internal.inject.SupplierInstanceBinding; +import org.jboss.weld.contexts.CreationalContextImpl; + +import java.util.Collection; +import java.util.IdentityHashMap; +import java.util.Map; + +/** + * Each Client Runtime has a unique CdiClientInjectionManager, which passes proper {@link Binding} to the Weld. + */ +public class CdiClientInjectionManager extends CdiInjectionManager { + + private Map clientInstanceBindings = new IdentityHashMap<>(); + private Map clientSupplierInstanceBindings + = new IdentityHashMap<>(); + private Map clientSupplierClassBinding = new IdentityHashMap<>(); + + public CdiClientInjectionManager(BeanManager beanManager, Binder bindings) { + super(beanManager, bindings); + } + + @Override + public void register(Binding binding) { + if (InstanceBinding.class.isInstance(binding)) { + final Collection preBindings = getBindings().getBindings(); + MatchableBinding.Matching matching = MatchableBinding.Matching.noneMatching(); + for (Binding preBinding : preBindings) { + if (InitializableInstanceBinding.class.isInstance(preBinding)) { + matching = matching.better(((InitializableInstanceBinding) preBinding).matches((InstanceBinding) binding)); + if (matching.isBest()) { + break; + } + } + } + if (matching.matches()) { + final InitializableInstanceBinding clone = matching.getBinding().clone(); + clone.init(((InstanceBinding) binding).getService()); + clientInstanceBindings.put(matching.getBinding(), clone); + } else { + throw new IllegalStateException("Not initialized " + ((InstanceBinding) binding).getService()); + } + } else if (SupplierInstanceBinding.class.isInstance(binding)) { + final Collection preBindings = getBindings().getBindings(); + MatchableBinding.Matching matching = MatchableBinding.Matching.noneMatching(); + for (Binding preBinding : preBindings) { + if (InitializableSupplierInstanceBinding.class.isInstance(preBinding)) { + matching = matching.better(((InitializableSupplierInstanceBinding) preBinding).matchesContracts(binding)); + if (matching.isBest()) { + break; + } + } + } + if (matching.matches()) { + final InitializableSupplierInstanceBinding clone = matching.getBinding().clone(); + clone.init(((SupplierInstanceBinding) binding).getSupplier()); + clientSupplierInstanceBindings.put(matching.getBinding(), clone); + } else { + throw new IllegalStateException("Not initialized " + ((SupplierInstanceBinding) binding).getSupplier()); + } +// } else if (SupplierClassBinding.class.isInstance(binding)) { +// final Collection preBindings = getBindings().getBindings(); +// BindingMatching.Matching matching = BindingMatching.Matching.noneMatching(); +// for (Binding preBinding : preBindings) { +// if (SupplierClassBinding.class.isInstance(preBinding)) { +// matching = matching.better(BindingMatching.matches(preBinding, binding)); +// if (matching.isBest()) { +// break; +// } +// } +// } +// if (matching.matches()) { +// final SupplierClassBinding clone = BindingCloner.clone((SupplierClassBinding) binding); +// clientSupplierClassBinding.put(matching.getBinding(), clone); +// } else { +// throw new IllegalStateException("Not initialized " + ((SupplierInstanceBinding) binding).getSupplier()); +// } + } + } + + public InitializableInstanceBinding getInjectionManagerBinding(InitializableInstanceBinding binding) { + InitializableInstanceBinding clientBinding = clientInstanceBindings.get(binding); + return clientBinding != null ? clientBinding : binding; + } + + public InitializableSupplierInstanceBinding getInjectionManagerBinding(InitializableSupplierInstanceBinding binding) { + InitializableSupplierInstanceBinding clientBinding = clientSupplierInstanceBindings.get(binding); + return clientBinding != null ? clientBinding : binding; + } + + public SupplierClassBinding getInjectionManagerBinding(SupplierClassBinding binding) { + SupplierClassBinding clientBinding = clientSupplierClassBinding.get(binding); + return clientBinding; + } + + @Override + public void shutdown() { + clientInstanceBindings.clear(); + } + + @Override + protected CreationalContext createCreationalContext(Bean bean) { + final CreationalContext ctx = new JerseyClientCreationalContext( + (CreationalContextImpl) super.createCreationalContext(bean)).setInjectionManager(this); + return ctx; + } + + @Override + public void completeRegistration() throws IllegalStateException { + register(Bindings.service(this).to(InjectionManager.class)); + } + + @Override + protected boolean isRuntimeTypeBean(Bean bean) { + return !JerseyBean.class.isInstance(bean) || ((JerseyBean) bean).getRutimeType() == RuntimeType.CLIENT; + } + +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/CdiInjectionManager.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/CdiInjectionManager.java new file mode 100644 index 0000000000..a99f2f05a1 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/CdiInjectionManager.java @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.InjectionTarget; +import javax.enterprise.inject.spi.Unmanaged; +import javax.inject.Singleton; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.inject.weld.internal.inject.InitializableInstanceBinding; +import org.glassfish.jersey.inject.weld.internal.inject.InitializableSupplierInstanceBinding; +import org.glassfish.jersey.inject.weld.internal.bean.JerseyBean; +import org.glassfish.jersey.inject.weld.internal.inject.MatchableBinding; +import org.glassfish.jersey.internal.inject.Binder; +import org.glassfish.jersey.internal.inject.Binding; +import org.glassfish.jersey.internal.inject.Bindings; +import org.glassfish.jersey.internal.inject.ClassBinding; +import org.glassfish.jersey.internal.inject.ForeignDescriptor; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.InstanceBinding; +import org.glassfish.jersey.internal.inject.ServiceHolder; +import org.glassfish.jersey.internal.inject.ServiceHolderImpl; +import org.glassfish.jersey.internal.inject.SupplierInstanceBinding; + +/** + * Implementation of {@link InjectionManager} used on the server side. + */ +@Singleton +public class CdiInjectionManager implements InjectionManager { + + private final BeanManager beanManager; + private final Binder bindings; + private boolean isCompleted = false; + Set> managedBeans; + + // Keeps all binders and bindings added to the InjectionManager during the bootstrap. + + public CdiInjectionManager(BeanManager beanManager, Binder bindings) { + this.beanManager = beanManager; + this.bindings = bindings; + } + + @Override + public void register(Binding binding) { + if (isManagedClass(binding)) { + return; + } + if (InstanceBinding.class.isInstance(binding)) { + final Collection preBindings = bindings.getBindings(); + MatchableBinding.Matching matching = MatchableBinding.Matching.noneMatching(); + for (Binding preBinding : preBindings) { + if (InitializableInstanceBinding.class.isInstance(preBinding)) { + matching = matching.better(((InitializableInstanceBinding) preBinding).matches((InstanceBinding) binding)); + if (matching.isBest()) { + break; + } + } + } + if (matching.matches()) { + matching.getBinding().init(((InstanceBinding) binding).getService()); + } else if (findClassBinding(binding.getImplementationType()) == null) { + throw new IllegalStateException("Not initialized " + ((InstanceBinding) binding).getService()); + } + } else if (SupplierInstanceBinding.class.isInstance(binding)) { + final Collection preBindings = bindings.getBindings(); + MatchableBinding.Matching matching = MatchableBinding.Matching.noneMatching(); + for (Binding preBinding : preBindings) { + if (InitializableSupplierInstanceBinding.class.isInstance(preBinding)) { + matching = matching.better( + ((InitializableSupplierInstanceBinding) preBinding).matches((SupplierInstanceBinding) binding)); + if (matching.isBest()) { + break; + } + } + } + if (matching.matches()) { + matching.getBinding().init(((SupplierInstanceBinding) binding).getSupplier()); + } else { + throw new IllegalStateException("Not initialized " + ((SupplierInstanceBinding) binding).getSupplier()); + } + } else if (ClassBinding.class.isInstance(binding)) { + if (findClassBinding(binding.getImplementationType()) == null) { +// final Collection preBindings = bindings.getBindings(); +// boolean found = false; +// for (Binding preBinding : preBindings) { +// if (ClassBinding.class.isInstance(preBinding) +// && ((ClassBinding) preBinding).getImplementationType() +// .equals(((ClassBinding) binding).getImplementationType())) { +// found = true; +// break; +// } +// } +// if (!found) { + throw new IllegalStateException("ClassBinding for " + binding.getImplementationType() + " not preregistered"); + } + } + } + + private ClassBinding findClassBinding(Class implementationType) { + final Collection preBindings = bindings.getBindings(); + boolean found = false; + for (Binding preBinding : preBindings) { + if (ClassBinding.class.isInstance(preBinding) + && ((ClassBinding) preBinding).getImplementationType().equals(implementationType)) { + return (ClassBinding) preBinding; + } + } + return null; + } + + private boolean isManagedClass(Binding binding) { + return managedBeans != null + && binding.getImplementationType() != null + && (managedBeans.contains(binding.getImplementationType()) + || (managedBeans.contains(binding.getImplementationType().getSuperclass()))); + } + + @Override + public void register(Iterable bindings) { + for (Binding binding : bindings) { + register(binding); + } + } + + @Override + public void register(Binder binder) { + for (Binding binding : Bindings.getBindings(this, binder)) { + register(binding); + } + } + + @Override + public void register(Object provider) throws IllegalArgumentException { + throw new IllegalArgumentException(LocalizationMessages.CDI_2_PROVIDER_NOT_REGISTRABLE(provider.getClass())); + } + + @Override + public boolean isRegistrable(Class clazz) { + return false; + } + + @Override + public T create(Class createMe) { + Unmanaged.UnmanagedInstance unmanaged = new Unmanaged<>(createMe).newInstance(); + return unmanaged.produce().get(); + } + + @Override + public T createAndInitialize(Class createMe) { + Unmanaged.UnmanagedInstance unmanaged = new Unmanaged<>(createMe).newInstance(); + return unmanaged.produce() + .inject() + .postConstruct() + .get(); + } + + @Override + @SuppressWarnings("unchecked") + public List> getAllServiceHolders(Class contractOrImpl, Annotation... qualifiers) { + List> result = new ArrayList<>(); + for (Bean bean : beanManager.getBeans(contractOrImpl, qualifiers)) { + + if (!isRuntimeTypeBean(bean)) { + continue; + } + + CreationalContext ctx = createCreationalContext(bean); + T reference = (T) beanManager.getReference(bean, contractOrImpl, ctx); + + int rank = 1; + if (bean instanceof JerseyBean) { + rank = ((JerseyBean) bean).getRank(); + } + + result.add(new ServiceHolderImpl<>(reference, (Class) bean.getBeanClass(), bean.getTypes(), rank)); + } + return result; + } + + @Override + public T getInstance(Class contractOrImpl, Annotation... qualifiers) { + return getInstanceInternal(contractOrImpl, qualifiers); + } + + @Override + public T getInstance(Class contractOrImpl) { + return getInstanceInternal(contractOrImpl); + } + + @Override + public T getInstance(Type contractOrImpl) { + return getInstanceInternal(contractOrImpl); + } + + @SuppressWarnings("unchecked") + protected T getInstanceInternal(Type contractOrImpl, Annotation... qualifiers) { +// if (contractOrImpl.getTypeName().contains("HelloResource")) { +// T t = (T) CDI.current().select((Class) contractOrImpl, qualifiers).get(); +// try { +// System.out.println(t.getClass().getMethod("hello").invoke(t)); +//// t.getClass().getMethod("hello").invoke(t); +// } catch (IllegalAccessException e) { +// e.printStackTrace(); +// } catch (InvocationTargetException e) { +// e.printStackTrace(); +// } catch (NoSuchMethodException e) { +// e.printStackTrace(); +// } +// return t; +// } + Set> beans = beanManager.getBeans(contractOrImpl, qualifiers); + if (beans.isEmpty()) { + return null; //ScopesTest + } + + final Iterator beansIterator = beans.iterator(); + Bean bean = (Bean) beansIterator.next(); + while (beansIterator.hasNext() && !JerseyBean.class.isInstance(bean) && !isRuntimeTypeBean(bean)) { + bean = (Bean) beansIterator.next(); // prefer Jersey binding + } + CreationalContext ctx = createCreationalContext((Bean) bean); + return (T) beanManager.getReference(bean, contractOrImpl, ctx); + } + + @Override + @SuppressWarnings("unchecked") + public Object getInstance(ForeignDescriptor foreignDescriptor) { + Bean bean = (Bean) foreignDescriptor.get(); + CreationalContext ctx = createCreationalContext(bean); + return bean.create(ctx); + } + + @Override + @SuppressWarnings("unchecked") + public ForeignDescriptor createForeignDescriptor(Binding binding) { + Class clazz; + if (ClassBinding.class.isAssignableFrom(binding.getClass())) { + clazz = ((ClassBinding) binding).getService(); + } else if (InstanceBinding.class.isAssignableFrom(binding.getClass())) { + clazz = ((InstanceBinding) binding).getService().getClass(); + } else { + throw new RuntimeException( + org.glassfish.jersey.internal.LocalizationMessages + .UNKNOWN_DESCRIPTOR_TYPE(binding.getClass().getSimpleName())); + } + + Set> beans = beanManager.getBeans(clazz); + if (beans.isEmpty()) { + return null; + } + + Bean bean = beans.iterator().next(); + CreationalContext ctx = createCreationalContext(bean); + return ForeignDescriptor.wrap(bean, instance -> bean.destroy(instance, ctx)); + } + + @Override + @SuppressWarnings("unchecked") + public List getAllInstances(Type contractOrImpl) { + List result = new ArrayList<>(); + for (Bean bean : beanManager.getBeans(contractOrImpl)) { + CreationalContext ctx = createCreationalContext(bean); + Object reference = beanManager.getReference(bean, contractOrImpl, ctx); + result.add((T) reference); + } + return result; + } + + @Override + @SuppressWarnings("unchecked") + public void inject(Object instance) { + AnnotatedType annotatedType = beanManager.createAnnotatedType((Class) instance.getClass()); + InjectionTarget injectionTarget = beanManager.createInjectionTarget(annotatedType); + CreationalContext context = createCreationalContext(null); + injectionTarget.inject(instance, context); + } + + @Override + public void preDestroy(Object preDestroyMe) { + } + + @Override + public void completeRegistration() throws IllegalStateException { +// bindings.bind(Bindings.service(this).to(InjectionManager.class)); +// bindings.install(new ContextInjectionResolverImpl.Binder(this::getBeanManager)); +// +// this.beanManager = new DefaultBeanManagerProvider().getBeanManager(); +// beanManager = new DefaultBeanManagerProvider().getBeanManager(); +// +// AbstractBinder masterBinder = beanManager.getExtension(SeBeanRegisterExtension.class).bindings; +// masterBinder.getBindings().addAll(bindings.getBindings()); + +// bindings.bind(Bindings.service(this).to(InjectionManager.class)); +// bindings.install(new ContextInjectionResolverImpl.Binder(this::getBeanManager)); + + if (!isCompleted) { + register(Bindings.service(this).to(InjectionManager.class)); + isCompleted = false; + } + } + + @Override + public void shutdown() { + + } + + protected Binder getBindings() { + return bindings; + } + + protected BeanManager getBeanManager() { + return beanManager; + } + + protected CreationalContext createCreationalContext(Bean bean) { + return (CreationalContext) beanManager.createCreationalContext(bean); + } + +// protected void registerInjectionResolver(InjectionResolverBinding injectionResolverBinding) { +// // beanManager.getExtension(BinderRegisterExtension.class).addInjectionResolver(injectionResolverBinding.getResolver()); +// } + + /** + * Identifies Jersey beans that are from different runtime (CLIENT vs SERVER). Used to exclude Jersey beans of incorrect + * {@link RuntimeType}. + * @param bean the given CDI bean. + * @return true iff the given bean is not a Jersey Bean or the Jersey Bean is of the proper {@code RuntimeType}. + */ + protected boolean isRuntimeTypeBean(Bean bean) { + return !JerseyBean.class.isInstance(bean) || ((JerseyBean) bean).getRutimeType() == RuntimeType.SERVER; + } + + @Override + public void inject(Object injectMe, String classAnalyzer) { + // TODO: Used only in legacy CDI integration. + throw new UnsupportedOperationException(); + } + + @Override + public T getInstance(Class contractOrImpl, String classAnalyzer) { + // TODO: Used only in legacy CDI integration. + throw new UnsupportedOperationException(); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/CdiInjectionManagerFactoryBase.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/CdiInjectionManagerFactoryBase.java new file mode 100644 index 0000000000..c0c48f6728 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/CdiInjectionManagerFactoryBase.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import javax.enterprise.inject.spi.BeanManager; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.InjectionManagerFactory; + +/** + * CDI Injection Manager Factory base class that holds the current bean manager. + */ +public abstract class CdiInjectionManagerFactoryBase implements InjectionManagerFactory { + private static BeanManager beanManager; + + /* package */ static void setBeanManager(BeanManager bm) { + beanManager = bm; + } + + protected static InjectionManager getInjectionManager(RuntimeType runtimeType) { + return beanManager.getExtension(BinderRegisterExtension.class).getInjectionManager(runtimeType); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/ClientBootstrapPreinitialization.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/ClientBootstrapPreinitialization.java new file mode 100644 index 0000000000..e82f0e0857 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/ClientBootstrapPreinitialization.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import javax.inject.Inject; +import javax.inject.Provider; +import javax.ws.rs.RuntimeType; +import javax.ws.rs.client.ClientBuilder; + +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientRequest; +import org.glassfish.jersey.client.JerseyClient; +import org.glassfish.jersey.inject.weld.spi.BootstrapPreinitialization; +import org.glassfish.jersey.internal.PropertiesDelegate; +import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.ReferencingFactory; +import org.glassfish.jersey.internal.util.collection.Ref; + +import java.util.function.Supplier; + +/** + * Jersey Client Runtime pre-initialization implementation. + */ +// TODO: put to a proper Jersey module +public class ClientBootstrapPreinitialization implements BootstrapPreinitialization { + + @Override + public void register(RuntimeType runtimeType, AbstractBinder binder) { + if (runtimeType == RuntimeType.SERVER) { + return; + } + + ClientConfig config = new ClientConfig(); + JerseyClient client = (JerseyClient) ClientBuilder.newClient(config); + client.getConfiguration().getClientExecutor(); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/ServerBootstrapPreinitialization.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/ServerBootstrapPreinitialization.java new file mode 100644 index 0000000000..a35f806b16 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/ServerBootstrapPreinitialization.java @@ -0,0 +1,649 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import javax.inject.Inject; +import javax.inject.Provider; +import javax.inject.Singleton; +import javax.servlet.Filter; +import javax.servlet.FilterConfig; +import javax.servlet.FilterRegistration; +import javax.servlet.RequestDispatcher; +import javax.servlet.Servlet; +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; +import javax.servlet.SessionCookieConfig; +import javax.servlet.SessionTrackingMode; +import javax.servlet.descriptor.JspConfigDescriptor; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.RuntimeType; +import javax.ws.rs.core.Configuration; +import javax.ws.rs.core.FeatureContext; +import javax.ws.rs.core.GenericType; + +import org.glassfish.grizzly.http.server.Request; +import org.glassfish.grizzly.http.server.Response; +import org.glassfish.jersey.inject.weld.spi.BootstrapPreinitialization; +import org.glassfish.jersey.internal.ServiceFinderBinder; +import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.Binding; +import org.glassfish.jersey.internal.inject.ClassBinding; +import org.glassfish.jersey.internal.inject.ReferencingFactory; +import org.glassfish.jersey.internal.spi.AutoDiscoverable; +import org.glassfish.jersey.internal.util.collection.Ref; +import org.glassfish.jersey.model.internal.CommonConfig; +import org.glassfish.jersey.model.internal.ComponentBag; +import org.glassfish.jersey.process.internal.RequestScoped; +import org.glassfish.jersey.server.wadl.WadlFeature; +import org.glassfish.jersey.server.wadl.processor.OptionsMethodProcessor; +import org.glassfish.jersey.server.wadl.processor.WadlModelProcessor; +import org.glassfish.jersey.servlet.WebConfig; +import org.glassfish.jersey.servlet.spi.AsyncContextDelegateProvider; +import org.glassfish.jersey.servlet.spi.FilterUrlMappingsProvider; + +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; +import java.util.EventListener; +import java.util.Map; +import java.util.Set; + + +/** + * Jersey server side pre-initialization implementation. + */ +// TODO : put to a proper module +public class ServerBootstrapPreinitialization implements BootstrapPreinitialization { + + + /** + * Referencing factory for Grizzly request. + */ + private static class GrizzlyRequestReferencingFactory extends ReferencingFactory { + + @Inject + public GrizzlyRequestReferencingFactory(final Provider> referenceFactory) { + super(referenceFactory); + } + } + + /** + * Referencing factory for Grizzly response. + */ + private static class GrizzlyResponseReferencingFactory extends ReferencingFactory { + + @Inject + public GrizzlyResponseReferencingFactory(final Provider> referenceFactory) { + super(referenceFactory); + } + } + + @SuppressWarnings("JavaDoc") + private static class HttpServletRequestReferencingFactory extends ReferencingFactory { + + @Inject + public HttpServletRequestReferencingFactory(final Provider> referenceFactory) { + super(referenceFactory); + } + } + + @SuppressWarnings("JavaDoc") + private static class HttpServletResponseReferencingFactory extends ReferencingFactory { + + @Inject + public HttpServletResponseReferencingFactory(final Provider> referenceFactory) { + super(referenceFactory); + } + } + + private static class WebConfigInitializer implements WebConfig { + + @Override + public ConfigType getConfigType() { + return ConfigType.ServletConfig; + } + + @Override + public ServletConfig getServletConfig() { + return new ServletConfig() { + @Override + public String getServletName() { + return "Preinit"; + } + + @Override + public ServletContext getServletContext() { + return WebConfigInitializer.this.getServletContext(); + } + + @Override + public String getInitParameter(String name) { + return null; + } + + @Override + public Enumeration getInitParameterNames() { + return null; + } + }; + } + + @Override + public FilterConfig getFilterConfig() { + return null; + } + + @Override + public String getName() { + return "Preinit"; + } + + @Override + public String getInitParameter(String name) { + return getName(); + } + + @Override + public Enumeration getInitParameterNames() { + return null; + } + + @Override + public ServletContext getServletContext() { + return new ServletContext() { + @Override + public String getContextPath() { + return null; + } + + @Override + public ServletContext getContext(String uripath) { + return WebConfigInitializer.this.getServletContext(); + } + + @Override + public int getMajorVersion() { + return 0; + } + + @Override + public int getMinorVersion() { + return 0; + } + + @Override + public int getEffectiveMajorVersion() { + return 0; + } + + @Override + public int getEffectiveMinorVersion() { + return 0; + } + + @Override + public String getMimeType(String file) { + return null; + } + + @Override + public Set getResourcePaths(String path) { + return null; + } + + @Override + public URL getResource(String path) throws MalformedURLException { + return null; + } + + @Override + public InputStream getResourceAsStream(String path) { + return null; + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + @Override + public RequestDispatcher getNamedDispatcher(String name) { + return null; + } + + @Override + public Servlet getServlet(String name) throws ServletException { + return null; + } + + @Override + public Enumeration getServlets() { + return null; + } + + @Override + public Enumeration getServletNames() { + return null; + } + + @Override + public void log(String msg) { + + } + + @Override + public void log(Exception exception, String msg) { + + } + + @Override + public void log(String message, Throwable throwable) { + + } + + @Override + public String getRealPath(String path) { + return null; + } + + @Override + public String getServerInfo() { + return null; + } + + @Override + public String getInitParameter(String name) { + return null; + } + + @Override + public Enumeration getInitParameterNames() { + return null; + } + + @Override + public boolean setInitParameter(String name, String value) { + return false; + } + + @Override + public Object getAttribute(String name) { + return null; + } + + @Override + public Enumeration getAttributeNames() { + return null; + } + + @Override + public void setAttribute(String name, Object object) { + + } + + @Override + public void removeAttribute(String name) { + + } + + @Override + public String getServletContextName() { + return null; + } + + @Override + public ServletRegistration.Dynamic addServlet(String servletName, String className) { + return null; + } + + @Override + public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { + return null; + } + + @Override + public ServletRegistration.Dynamic addServlet(String servletName, Class servletClass) { + return null; + } + + @Override + public ServletRegistration.Dynamic addJspFile(String servletName, String jspFile) { + return null; + } + + @Override + public T createServlet(Class clazz) throws ServletException { + return null; + } + + @Override + public ServletRegistration getServletRegistration(String servletName) { + return null; + } + + @Override + public Map getServletRegistrations() { + return null; + } + + @Override + public FilterRegistration.Dynamic addFilter(String filterName, String className) { + return null; + } + + @Override + public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) { + return null; + } + + @Override + public FilterRegistration.Dynamic addFilter(String filterName, Class filterClass) { + return null; + } + + @Override + public T createFilter(Class clazz) throws ServletException { + return null; + } + + @Override + public FilterRegistration getFilterRegistration(String filterName) { + return null; + } + + @Override + public Map getFilterRegistrations() { + return null; + } + + @Override + public SessionCookieConfig getSessionCookieConfig() { + return null; + } + + @Override + public void setSessionTrackingModes(Set sessionTrackingModes) { + + } + + @Override + public Set getDefaultSessionTrackingModes() { + return null; + } + + @Override + public Set getEffectiveSessionTrackingModes() { + return null; + } + + @Override + public void addListener(String className) { + + } + + @Override + public void addListener(T t) { + + } + + @Override + public void addListener(Class listenerClass) { + + } + + @Override + public T createListener(Class clazz) throws ServletException { + return null; + } + + @Override + public JspConfigDescriptor getJspConfigDescriptor() { + return null; + } + + @Override + public ClassLoader getClassLoader() { + return null; + } + + @Override + public void declareRoles(String... roleNames) { + + } + + @Override + public String getVirtualServerName() { + return null; + } + + @Override + public int getSessionTimeout() { + return 0; + } + + @Override + public void setSessionTimeout(int sessionTimeout) { + + } + + @Override + public String getRequestCharacterEncoding() { + return null; + } + + @Override + public void setRequestCharacterEncoding(String encoding) { + + } + + @Override + public String getResponseCharacterEncoding() { + return null; + } + + @Override + public void setResponseCharacterEncoding(String encoding) { + + } + }; + } + } + + private static class PreinitializationFeatureContext implements FeatureContext { + + private final AbstractBinder binder; + + private PreinitializationFeatureContext(AbstractBinder binder) { + this.binder = binder; + } + + @Override + public Configuration getConfiguration() { + return new CommonConfig(RuntimeType.SERVER, ComponentBag.INCLUDE_ALL); + } + + @Override + public FeatureContext property(String name, Object value) { + return this; + } + + @Override + public FeatureContext register(Class componentClass) { + binder.bindAsContract(componentClass); + return this; + } + + @Override + public FeatureContext register(Class componentClass, int priority) { + binder.bindAsContract(componentClass).ranked(priority); + return this; + } + + @Override + public FeatureContext register(Class componentClass, Class... contracts) { + final ClassBinding binding = binder.bind(componentClass); + if (contracts != null) { + for (Class contract : contracts) { + binding.to(contract); + } + } + return this; + } + + @Override + public FeatureContext register(Class componentClass, Map, Integer> contracts) { + for (Map.Entry, Integer> contract : contracts.entrySet()) { + final AbstractBinder abstractBinder = new AbstractBinder() { + @Override + protected void configure() { + } + }; + final ClassBinding binding = abstractBinder.bind(componentClass); + binding.to(contract.getKey()).ranked(contract.getValue()); + binder.install(abstractBinder); + } + return this; + } + + @Override + public FeatureContext register(Object component) { + if (AbstractBinder.class.isInstance(component)) { + binder.install((AbstractBinder) component); + } else { + binder.bind(component).to(component.getClass()); + } + return this; + } + + @Override + public FeatureContext register(Object component, int priority) { + binder.bind(component).to(component.getClass()).ranked(priority); + return this; + } + + @Override + public FeatureContext register(Object component, Class... contracts) { + Binding binding = binder.bind(component); + if (contracts != null) { + for (Class contract : contracts) { + binding.to(contract); + } + } + return this; + } + + @Override + public FeatureContext register(Object component, Map, Integer> contracts) { + for (Map.Entry, Integer> contract : contracts.entrySet()) { + final AbstractBinder abstractBinder = new AbstractBinder() { + @Override + protected void configure() { + } + }; + final Binding binding = abstractBinder.bind(component); + binding.to(contract.getKey()).ranked(contract.getValue()); + binder.install(abstractBinder); + } + return this; + } + } + + @Override + public void register(RuntimeType runtimeType, AbstractBinder binder) { +// binder.install(new MessagingBinders.MessageBodyProviders(null, RuntimeType.SERVER), +// new MessagingBinders.HeaderDelegateProviders()); +// +// // Server Binder +// binder.install(new MappableExceptionWrapperInterceptor.Binder(), +// new MonitoringContainerListener.Binder()); +// binder.bind(ChunkedResponseWriter.class).to(MessageBodyWriter.class).in(Singleton.class); +// binder.bind(JsonWithPaddingInterceptor.class).to(WriterInterceptor.class).in(Singleton.class); + + if (runtimeType == RuntimeType.SERVER) { + // new ApplicationHandler(new ResourceConfig()); + + //grizzly + binder.bindFactory(GrizzlyRequestReferencingFactory.class).to(Request.class) + .proxy(false).in(RequestScoped.class); + binder.bindFactory(ReferencingFactory.referenceFactory()).to(new GenericType>() {}) + .in(RequestScoped.class); + + binder.bindFactory(GrizzlyResponseReferencingFactory.class).to(Response.class) + .proxy(true).proxyForSameScope(false).in(RequestScoped.class); + binder.bindFactory(ReferencingFactory.referenceFactory()).to(new GenericType>() {}) + .in(RequestScoped.class); + + // servlet + binder.bindFactory(HttpServletRequestReferencingFactory.class).to(HttpServletRequest.class) + .proxy(true).proxyForSameScope(false).in(RequestScoped.class); + + binder.bindFactory(ReferencingFactory.referenceFactory()) + .to(new GenericType>() {}).in(RequestScoped.class); + + binder.bindFactory(HttpServletResponseReferencingFactory.class).to(HttpServletResponse.class) + .proxy(true).proxyForSameScope(false).in(RequestScoped.class); + binder.bindFactory(ReferencingFactory.referenceFactory()) + .to(new GenericType>() {}).in(RequestScoped.class); + + final WebConfig webConfig = new WebConfigInitializer(); + final Map applicationProperties = Collections.EMPTY_MAP; + + binder.bindFactory(() -> webConfig.getServletContext()).to(ServletContext.class).in(Singleton.class); + binder.bindFactory(() -> webConfig).to(WebConfig.class).in(Singleton.class); + binder.install( + new ServiceFinderBinder<>(AsyncContextDelegateProvider.class, applicationProperties, RuntimeType.SERVER)); + binder.install( + new ServiceFinderBinder<>(FilterUrlMappingsProvider.class, applicationProperties, RuntimeType.SERVER)); + + final ServletConfig servletConfig = webConfig.getServletConfig(); + binder.bindFactory(() -> servletConfig).to(ServletConfig.class).in(Singleton.class); + + // WADL TODO put to a proper module + try { + new WadlFeature().configure(new PreinitializationFeatureContext(binder) { + @Override + public FeatureContext register(Class componentClass) { + if (WadlModelProcessor.class.isAssignableFrom(componentClass)) { + super.register(WadlModelProcessor.OptionsHandler.class); + } + super.register(componentClass); + return this; + } + }); + } catch (Exception e) { + + } + try { + Class[] classes = OptionsMethodProcessor.class.getDeclaredClasses(); + for (Class clz : classes) { + binder.bindAsContract(clz); + } + } catch (Exception e) { + + } + } + +// +// //ApplicationConfigurator +// binder.bind(new InitializableInstanceBinding((Application) null).to(Application.class)); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/WrappingInjectionManager.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/WrappingInjectionManager.java new file mode 100644 index 0000000000..a3bea1ae43 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/managed/WrappingInjectionManager.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import org.glassfish.jersey.internal.inject.Binder; +import org.glassfish.jersey.internal.inject.Binding; +import org.glassfish.jersey.internal.inject.ForeignDescriptor; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.ServiceHolder; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.List; + +/** + * Holder of an InjectionManager that is referenced by Beans created in bootstrap. + * The original bootstrap injection manager is replaced by the proper injection manager + * after the pre-initialization phase. + */ +class WrappingInjectionManager implements InjectionManager { + + private InjectionManager injectionManager; + + WrappingInjectionManager setInjectionManager(InjectionManager injectionManager) { + this.injectionManager = injectionManager; + return this; + } + + @Override + public void completeRegistration() { + injectionManager.completeRegistration(); + } + + @Override + public void shutdown() { + injectionManager.shutdown(); + } + + @Override + public void register(Binding binding) { + injectionManager.register(binding); + } + + @Override + public void register(Iterable descriptors) { + injectionManager.register(descriptors); + } + + @Override + public void register(Binder binder) { + injectionManager.register(binder); + } + + @Override + public void register(Object provider) throws IllegalArgumentException { + injectionManager.register(provider); + } + + @Override + public boolean isRegistrable(Class clazz) { + return injectionManager.isRegistrable(clazz); + } + + @Override + public T create(Class createMe) { + return injectionManager.create(createMe); + } + + @Override + public T createAndInitialize(Class createMe) { + return injectionManager.createAndInitialize(createMe); + } + + @Override + public List> getAllServiceHolders(Class contractOrImpl, Annotation... qualifiers) { + return injectionManager.getAllServiceHolders(contractOrImpl, qualifiers); + } + + @Override + public T getInstance(Class contractOrImpl, Annotation... qualifiers) { + return injectionManager.getInstance(contractOrImpl, qualifiers); + } + + @Override + public T getInstance(Class contractOrImpl, String classAnalyzer) { + return injectionManager.getInstance(contractOrImpl, classAnalyzer); + } + + @Override + public T getInstance(Class contractOrImpl) { + return injectionManager.getInstance(contractOrImpl); + } + + @Override + public T getInstance(Type contractOrImpl) { + return injectionManager.getInstance(contractOrImpl); + } + + @Override + public Object getInstance(ForeignDescriptor foreignDescriptor) { + return injectionManager.getInstance(foreignDescriptor); + } + + @Override + public ForeignDescriptor createForeignDescriptor(Binding binding) { + return injectionManager.createForeignDescriptor(binding); + } + + @Override + public List getAllInstances(Type contractOrImpl) { + return injectionManager.getAllInstances(contractOrImpl); + } + + @Override + public void inject(Object injectMe) { + injectionManager.inject(injectMe); + } + + @Override + public void inject(Object injectMe, String classAnalyzer) { + injectionManager.inject(injectMe, classAnalyzer); + } + + @Override + public void preDestroy(Object preDestroyMe) { + injectionManager.preDestroy(preDestroyMe); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/CdiRequestContext.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/CdiRequestContext.java new file mode 100644 index 0000000000..64b8ebce1c --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/CdiRequestContext.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.scope; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.glassfish.jersey.internal.util.ExtendedLogger; +import org.glassfish.jersey.internal.util.LazyUid; +import org.glassfish.jersey.process.internal.RequestContext; + +/** + * Implementation of the request context. + */ +public final class CdiRequestContext implements RequestContext { + + private static final ExtendedLogger logger = + new ExtendedLogger(Logger.getLogger(CdiRequestContext.class.getName()), Level.FINEST); + + /* + * Scope instance UUID. + * + * For performance reasons, it's only generated if toString() method is invoked, + * e.g. as part of some low-level logging. + */ + private final LazyUid id = new LazyUid(); + + /** + * Holds the number of snapshots of this scope. + */ + private final AtomicInteger referenceCounter; + + /** + * A map of injectable instances in this scope. + */ + private final Map store; + + CdiRequestContext() { + this.store = new HashMap<>(); + this.referenceCounter = new AtomicInteger(1); + } + + Map getStore() { + return store; + } + + /** + * Get a "new" reference of the scope instance. This will increase + * the internal reference counter which prevents the scope instance + * to be destroyed until a {@link #release()} method is explicitly + * called (once per each {@code getReference()} method call). + * + * @return referenced scope instance. + */ + @Override + public RequestContext getReference() { + // TODO: replace counter with a phantom reference + reference queue-based solution + referenceCounter.incrementAndGet(); + return this; + } + + /** + * Release a single reference to the current request scope instance. + *

          + * Once all instance references are released, the instance will be recycled. + */ + @Override + public void release() { + if (referenceCounter.decrementAndGet() < 1) { + try { + store.clear(); + } finally { + logger.debugLog("Released scope instance {0}", this); + } + } + } + + @Override + public String toString() { + return "Instance{" + + "id=" + id + + ", referenceCounter=" + referenceCounter + + ", store size=" + store.size() + + '}'; + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/CdiRequestScope.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/CdiRequestScope.java new file mode 100644 index 0000000000..64d2a99ebb --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/CdiRequestScope.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.scope; + +import javax.inject.Inject; + +import org.glassfish.jersey.process.internal.RequestContext; +import org.glassfish.jersey.process.internal.RequestScope; + +import org.jboss.weld.context.bound.BoundRequestContext; + +import java.util.concurrent.Callable; + +/** + * CDI Request scope implementation using Weld-specific {@link BoundRequestContext} which allows pass on storage for + * request-scoped objects. + */ +public class CdiRequestScope extends RequestScope { + + @Inject + private BoundRequestContext requestContextController; + + @Override + public RequestContext createContext() { + return new CdiRequestContext(); + } + + @Override + protected void activate(RequestContext context, RequestContext oldContext) { + super.activate(context, oldContext); + + if (oldContext != null) { + CdiRequestContext oldRequestContext = (CdiRequestContext) oldContext; + requestContextController.deactivate(); + requestContextController.dissociate(oldRequestContext.getStore()); + } + + CdiRequestContext cdiRequestContext = (CdiRequestContext) context; + requestContextController.associate(cdiRequestContext.getStore()); + requestContextController.activate(); + } + + @Override + protected void resume(RequestContext context) { + super.resume(context); + + if (context != null) { + CdiRequestContext cdiRequestContext = (CdiRequestContext) context; + requestContextController.associate(cdiRequestContext.getStore()); + requestContextController.activate(); + } + } + + @Override + protected void release(RequestContext context) { + super.release(context); + + CdiRequestContext cdiRequestContext = (CdiRequestContext) context; + requestContextController.invalidate(); + requestContextController.deactivate(); + requestContextController.dissociate(cdiRequestContext.getStore()); + } + + @Override + protected void suspend(RequestContext context) { + if (context != null) { + CdiRequestContext cdiRequestContext = (CdiRequestContext) context; + requestContextController.deactivate(); + requestContextController.dissociate(cdiRequestContext.getStore()); + } + } + + @Override + public T runInScope(Callable task) throws Exception { + return super.runInScope(task); + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/RequestScopeBean.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/RequestScopeBean.java new file mode 100644 index 0000000000..6770060bfa --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/scope/RequestScopeBean.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.scope; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.Any; +import javax.enterprise.inject.Default; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.enterprise.inject.spi.InjectionTarget; +import javax.enterprise.util.AnnotationLiteral; +import javax.inject.Singleton; + +import org.glassfish.jersey.process.internal.RequestScope; + +/** + * CDI Class Bean represents {@link CdiRequestScope}. + * + * @author Petr Bouda + */ +public class RequestScopeBean implements Bean { + + private final InjectionTarget injectionTarget; + + /** + * Creates a new Jersey-specific {@link javax.enterprise.inject.spi.Bean} instance. + */ + public RequestScopeBean(BeanManager beanManager) { + AnnotatedType annotatedType = beanManager.createAnnotatedType(CdiRequestScope.class); + this.injectionTarget = beanManager.createInjectionTarget(annotatedType); + } + + @Override + public boolean isNullable() { + return false; + } + + @Override + @SuppressWarnings("unchecked") + public CdiRequestScope create(CreationalContext context) { + CdiRequestScope instance = injectionTarget.produce(context); + injectionTarget.inject(instance, context); + injectionTarget.postConstruct(instance); + return instance; + } + + @Override + public void destroy(CdiRequestScope instance, CreationalContext context) { + injectionTarget.preDestroy(instance); + injectionTarget.dispose(instance); + context.release(); + } + + @Override + public Set getTypes() { + return Collections.singleton(RequestScope.class); + } + + @Override + public Set getInjectionPoints() { + return Collections.emptySet(); + } + + @Override + public Set getQualifiers() { + Set qualifiers = new HashSet<>(); + qualifiers.add(new AnnotationLiteral() {}); + qualifiers.add(new AnnotationLiteral() {}); + return qualifiers; + } + + @Override + public Class getScope() { + return Singleton.class; + } + + @Override + public String getName() { + return CdiRequestScope.class.getName(); + } + + @Override + public Set> getStereotypes() { + return Collections.emptySet(); + } + + @Override + public boolean isAlternative() { + return false; + } + + @Override + public Class getBeanClass() { + return CdiRequestScope.class; + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/type/GenericArrayTypeImpl.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/type/GenericArrayTypeImpl.java new file mode 100644 index 0000000000..d093e261f8 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/type/GenericArrayTypeImpl.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.type; + +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Type; + +/** + * An implementation of GenericArrayType for those times we need to create this on the fly. + * + * @author John Wells (john.wells at oracle.com) + */ +public class GenericArrayTypeImpl implements GenericArrayType { + private final Type genericComponentType; + + /** + * Creates the GenericArrayType with the given array type + * + * @param gct the non-null type for this GenericArrayType + */ + public GenericArrayTypeImpl(Type gct) { + genericComponentType = gct; + } + + /* (non-Javadoc) + * @see java.lang.reflect.GenericArrayType#getGenericComponentType() + */ + @Override + public Type getGenericComponentType() { + return genericComponentType; + } + + @Override + public int hashCode() { + return genericComponentType.hashCode(); + } + + /** + * Returns true if a is equals to b, or both + * and and b are null. Is safe even if + * a or b is null. If a or b is null but + * the other is not null, this returns false + * + * @param a A possibly null object to compare + * @param b A possibly null object to compare + * @return true if equal, false if not + */ + private static boolean safeEquals(Object a, Object b) { + if (a == b) { + return true; + } + if (a == null) { + return false; + } + if (b == null) { + return false; + } + + return a.equals(b); + } + + @Override + public boolean equals(Object o) { + if (o == null) { + return false; + } + if (!(o instanceof GenericArrayType)) { + return false; + } + + GenericArrayType other = (GenericArrayType) o; + + return safeEquals(genericComponentType, other.getGenericComponentType()); + } + + public String toString() { + return "GenericArrayTypeImpl(" + genericComponentType + ")"; + } + +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/type/ParameterizedTypeImpl.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/type/ParameterizedTypeImpl.java new file mode 100644 index 0000000000..c358aa2bb3 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/internal/type/ParameterizedTypeImpl.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.type; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; + +/** + * Simple implementation of {@link ParameterizedType}. + *

          + * John Wells (john.wells at oracle.com) + */ +public class ParameterizedTypeImpl implements ParameterizedType { + + private final Type rawType; + private final Type actualTypeArguments[]; + + /** + * A new parameterized type. + * + * @param rawType The raw type of this type. + * @param actualTypeArguments The actual type arguments. + */ + public ParameterizedTypeImpl(Type rawType, Type... actualTypeArguments) { + this.rawType = rawType; + this.actualTypeArguments = actualTypeArguments; + } + + /* (non-Javadoc) + * @see java.lang.reflect.ParameterizedType#getActualTypeArguments() + */ + @Override + public Type[] getActualTypeArguments() { + return actualTypeArguments; + } + + /* (non-Javadoc) + * @see java.lang.reflect.ParameterizedType#getRawType() + */ + @Override + public Type getRawType() { + return rawType; + } + + /* (non-Javadoc) + * @see java.lang.reflect.ParameterizedType#getOwnerType() + * This is only used for top level types + */ + @Override + public Type getOwnerType() { + return null; + } + + @Override + public int hashCode() { + int retVal = Arrays.hashCode(actualTypeArguments); + if (rawType == null) { + return retVal; + } + return retVal ^ rawType.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o == null) { + return false; + } + if (!(o instanceof ParameterizedType)) { + return false; + } + + ParameterizedType other = (ParameterizedType) o; + + if (!rawType.equals(other.getRawType())) { + return false; + } + + Type otherActuals[] = other.getActualTypeArguments(); + + if (otherActuals.length != actualTypeArguments.length) { + return false; + } + + for (int lcv = 0; lcv < otherActuals.length; lcv++) { + if (!actualTypeArguments[lcv].equals(otherActuals[lcv])) { + return false; + } + } + + return true; + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/managed/CdiInjectionManagerFactory.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/managed/CdiInjectionManagerFactory.java new file mode 100644 index 0000000000..8ea746f9af --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/managed/CdiInjectionManagerFactory.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.managed; + +import javax.annotation.Priority; +import javax.enterprise.context.spi.CreationalContext; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.inject.weld.internal.injector.JerseyClientCreationalContext; +import org.glassfish.jersey.inject.weld.internal.managed.CdiInjectionManagerFactoryBase; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.InjectionManagerFactory; +import org.glassfish.jersey.server.ApplicationHandler; + +/** + * SPI implementation of {@link InjectionManagerFactory} which provides a new instance of CDI {@link InjectionManager}. + */ +@Priority(20) +public class CdiInjectionManagerFactory extends CdiInjectionManagerFactoryBase implements InjectionManagerFactory { + + @Override + // TODO deprecate this in favor of #create(Object parent, RuntimeType runtimeType) + public InjectionManager create(Object parent) { + return create(parent, getRuntimeType()); + } + + /** + * Create injectionManager for {@link RuntimeType#CLIENT or get the existing injection manager for the server} + * @param parent Parent injection manager. Not used in this InjectionManagerFactory. + * @param runtimeType {@link RuntimeType} to get or create the proper injection manager. + * @return The required injection manager instance. + */ + public InjectionManager create(Object parent, RuntimeType runtimeType) { + return getInjectionManager(runtimeType); + } + + /** + * Get the client side InjectionManager stored in the {@link CreationalContext} or the server side InjectionManager. + * @param creationalContext {@link CreationalContext} subclass which may hold InjectionManager for the client + * @return existing client side injection or server side injection manager. + */ + public static InjectionManager getInjectionManager(CreationalContext creationalContext) { + if (JerseyClientCreationalContext.class.isInstance(creationalContext)) { + return ((JerseyClientCreationalContext) creationalContext).getInjectionManager(); + } else { + return getInjectionManager(RuntimeType.SERVER); + } + } + + // TODO refactor to call InjectionManagerFactory#create(Object, RuntimeType); + private static RuntimeType getRuntimeType() { + Exception e = new RuntimeException(); + for (StackTraceElement element : e.getStackTrace()) { + if (element.getClassName().equals(ClientConfig.class.getName())) { + return RuntimeType.CLIENT; + } + if (element.getClassName().equals(ApplicationHandler.class.getName())) { + return RuntimeType.SERVER; + } + } + return RuntimeType.SERVER; + } +} diff --git a/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/spi/BootstrapPreinitialization.java b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/spi/BootstrapPreinitialization.java new file mode 100644 index 0000000000..0c90da51a8 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/java/org/glassfish/jersey/inject/weld/spi/BootstrapPreinitialization.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.spi; + +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.Beta; +import org.glassfish.jersey.internal.inject.AbstractBinder; + +/** + *

          + * The entry point for pre-initialize Jersey during bootstrap. Register the beans that are not recognized by the injection + * framework to be injected in runtime. Register beans for the specific runtime type into the {@link AbstractBinder}. + *

          + */ +@Beta +public interface BootstrapPreinitialization { + /** + * Manually register beans that are not automatically recognised by the injection framework. + * @param runtimeType + * @param binder + */ + void register(RuntimeType runtimeType, AbstractBinder binder); +} diff --git a/incubator/cdi-inject-weld/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/incubator/cdi-inject-weld/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension new file mode 100644 index 0000000000..729ccf2377 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension @@ -0,0 +1,17 @@ + +# Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License v. 2.0, which is available at +# http://www.eclipse.org/legal/epl-2.0. +# +# This Source Code may also be made available under the following Secondary +# Licenses when the conditions for such availability set forth in the +# Eclipse Public License v. 2.0 are satisfied: GNU General Public License, +# version 2 with the GNU Classpath Exception, which is available at +# https://www.gnu.org/software/classpath/license.html. +# +# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +# + +org.glassfish.jersey.inject.weld.internal.managed.BinderRegisterExtension \ No newline at end of file diff --git a/incubator/cdi-inject-weld/src/main/resources/META-INF/services/org.glassfish.jersey.inject.weld.spi.BootstrapPreinitialization b/incubator/cdi-inject-weld/src/main/resources/META-INF/services/org.glassfish.jersey.inject.weld.spi.BootstrapPreinitialization new file mode 100644 index 0000000000..9e2e9adce4 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/resources/META-INF/services/org.glassfish.jersey.inject.weld.spi.BootstrapPreinitialization @@ -0,0 +1,17 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License v. 2.0, which is available at +# http://www.eclipse.org/legal/epl-2.0. +# +# This Source Code may also be made available under the following Secondary +# Licenses when the conditions for such availability set forth in the +# Eclipse Public License v. 2.0 are satisfied: GNU General Public License, +# version 2 with the GNU Classpath Exception, which is available at +# https://www.gnu.org/software/classpath/license.html. +# +# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +# +org.glassfish.jersey.inject.weld.internal.managed.ClientBootstrapPreinitialization +org.glassfish.jersey.inject.weld.internal.managed.ServerBootstrapPreinitialization \ No newline at end of file diff --git a/incubator/cdi-inject-weld/src/main/resources/META-INF/services/org.glassfish.jersey.internal.inject.InjectionManagerFactory b/incubator/cdi-inject-weld/src/main/resources/META-INF/services/org.glassfish.jersey.internal.inject.InjectionManagerFactory new file mode 100644 index 0000000000..71491fa8a9 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/resources/META-INF/services/org.glassfish.jersey.internal.inject.InjectionManagerFactory @@ -0,0 +1,17 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License v. 2.0, which is available at +# http://www.eclipse.org/legal/epl-2.0. +# +# This Source Code may also be made available under the following Secondary +# Licenses when the conditions for such availability set forth in the +# Eclipse Public License v. 2.0 are satisfied: GNU General Public License, +# version 2 with the GNU Classpath Exception, which is available at +# https://www.gnu.org/software/classpath/license.html. +# +# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +# + +org.glassfish.jersey.inject.weld.managed.CdiInjectionManagerFactory diff --git a/incubator/cdi-inject-weld/src/main/resources/org/glassfish/jersey/inject/weld/internal/managed/localization.properties b/incubator/cdi-inject-weld/src/main/resources/org/glassfish/jersey/inject/weld/internal/managed/localization.properties new file mode 100644 index 0000000000..8716ee6bb8 --- /dev/null +++ b/incubator/cdi-inject-weld/src/main/resources/org/glassfish/jersey/inject/weld/internal/managed/localization.properties @@ -0,0 +1,18 @@ +# +# Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License v. 2.0, which is available at +# http://www.eclipse.org/legal/epl-2.0. +# +# This Source Code may also be made available under the following Secondary +# Licenses when the conditions for such availability set forth in the +# Eclipse Public License v. 2.0 are satisfied: GNU General Public License, +# version 2 with the GNU Classpath Exception, which is available at +# https://www.gnu.org/software/classpath/license.html. +# +# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +# + +# {0} - full classname +cdi2.provider.not.registrable=Provider registered to CdiInjectionManager cannot be process because of incompatible type: {0}. diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/injector/CachedConstructorAnalyzerTest.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/injector/CachedConstructorAnalyzerTest.java new file mode 100644 index 0000000000..eebc20decc --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/injector/CachedConstructorAnalyzerTest.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.Collection; + +import javax.enterprise.inject.InjectionException; +import javax.ws.rs.MatrixParam; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.Context; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +/** + * Tests {@link CachedConstructorAnalyzer}. + */ +public class CachedConstructorAnalyzerTest { + + private static final Collection> ANNOTATIONS = + Arrays.asList(Context.class, PathParam.class); + + @Test + public void testDefaultConstructor() { + CachedConstructorAnalyzer analyzer = + new CachedConstructorAnalyzer<>(DefaultConstructor.class, ANNOTATIONS); + + assertEquals(0, analyzer.getConstructor().getParameterCount()); + } + + @Test + public void testNoArgsConstructor() { + CachedConstructorAnalyzer analyzer = + new CachedConstructorAnalyzer<>(NoArgsConstructor.class, ANNOTATIONS); + + assertEquals(0, analyzer.getConstructor().getParameterCount()); + } + + @Test + public void testSingleAnnotatedConstructor() { + CachedConstructorAnalyzer analyzer = + new CachedConstructorAnalyzer<>(SingleAnnotatedConstructor.class, ANNOTATIONS); + + assertEquals(1, analyzer.getConstructor().getParameterCount()); + } + + @Test + public void testSingleMultiAnnotatedConstructor() { + CachedConstructorAnalyzer analyzer = + new CachedConstructorAnalyzer<>(SingleMultiAnnotatedConstructor.class, ANNOTATIONS); + + assertEquals(2, analyzer.getConstructor().getParameterCount()); + } + + @Test + public void testLargestAnnotatedConstructor() { + CachedConstructorAnalyzer analyzer = + new CachedConstructorAnalyzer<>(LargestAnnotatedConstructor.class, ANNOTATIONS); + + assertEquals(3, analyzer.getConstructor().getParameterCount()); + } + + @Test + public void testContainsSmallerNonAnnotatedConstructor() { + CachedConstructorAnalyzer analyzer = + new CachedConstructorAnalyzer<>(ContainsSmallerNonAnnotatedConstructor.class, ANNOTATIONS); + + assertEquals(2, analyzer.getConstructor().getParameterCount()); + } + + @Test + public void testContainsLargerNonAnnotatedConstructor() { + CachedConstructorAnalyzer analyzer = + new CachedConstructorAnalyzer<>(ContainsLargerNonAnnotatedConstructor.class, ANNOTATIONS); + + assertEquals(1, analyzer.getConstructor().getParameterCount()); + } + + @Test + public void testSameNonAnnotatedConstructor() { + CachedConstructorAnalyzer analyzer = + new CachedConstructorAnalyzer<>(SameNonAnnotatedConstructor.class, ANNOTATIONS); + + assertEquals(1, analyzer.getConstructor().getParameterCount()); + } + + @Test + public void testBothAnnotatedConstructor() { + CachedConstructorAnalyzer analyzer = + new CachedConstructorAnalyzer<>(BothAnnotatedConstructor.class, ANNOTATIONS); + + Constructor constructor = analyzer.getConstructor(); + assertEquals(1, constructor.getParameterCount()); + assertEquals(Integer.class, constructor.getParameterTypes()[0]); + } + + @Test + public void testOneNonAnnotatedConstructor() { + CachedConstructorAnalyzer analyzer = + new CachedConstructorAnalyzer<>(OneNonAnnotatedConstructor.class, ANNOTATIONS); + + assertEquals(1, analyzer.getConstructor().getParameterCount()); + } + + @Test + public void testMultiAnnotatedConstructor() { + CachedConstructorAnalyzer analyzer = + new CachedConstructorAnalyzer<>(MultiAnnotatedConstructor.class, ANNOTATIONS); + + assertEquals(2, analyzer.getConstructor().getParameterCount()); + } + + @Test(expected = InjectionException.class) + public void testUnknownAnnotatedConstructor() { + new CachedConstructorAnalyzer<>(UnknownAnnotatedConstructor.class, ANNOTATIONS).getConstructor(); + } + + @Test(expected = InjectionException.class) + public void testSingleNonAnnotatedConstructor() { + new CachedConstructorAnalyzer<>(SingleNonAnnotatedConstructor.class, ANNOTATIONS).getConstructor(); + } + + public static class DefaultConstructor { + } + + public static class NoArgsConstructor { + public NoArgsConstructor() { + } + } + + public static class SingleNonAnnotatedConstructor { + public SingleNonAnnotatedConstructor(String str) { + } + } + + public static class SingleAnnotatedConstructor { + public SingleAnnotatedConstructor(@Context String str) { + } + } + + public static class SingleMultiAnnotatedConstructor { + public SingleMultiAnnotatedConstructor(@Context String str, @PathParam("name") String name) { + } + } + + public static class LargestAnnotatedConstructor { + public LargestAnnotatedConstructor(@Context String str, @PathParam("name") String name, @Context String str2) { + } + + public LargestAnnotatedConstructor(@Context String str) { + } + + public LargestAnnotatedConstructor(@Context String str, @PathParam("name") String name) { + } + } + + public static class ContainsSmallerNonAnnotatedConstructor { + public ContainsSmallerNonAnnotatedConstructor(String str) { + } + + public ContainsSmallerNonAnnotatedConstructor(@Context String str, @PathParam("name") String name) { + } + } + + public static class ContainsLargerNonAnnotatedConstructor { + public ContainsLargerNonAnnotatedConstructor(@Context String str) { + } + + public ContainsLargerNonAnnotatedConstructor(String str, String name) { + } + } + + public static class SameNonAnnotatedConstructor { + public SameNonAnnotatedConstructor(@Context String str) { + } + + public SameNonAnnotatedConstructor(Integer name) { + } + } + + public static class BothAnnotatedConstructor { + public BothAnnotatedConstructor(@Context String str) { + } + + public BothAnnotatedConstructor(@Context Integer name) { + } + } + + public static class OneNonAnnotatedConstructor { + public OneNonAnnotatedConstructor(@Context String str) { + } + + public OneNonAnnotatedConstructor(@Context Integer name, String str) { + } + } + + public static class MultiAnnotatedConstructor { + public MultiAnnotatedConstructor(@Context Integer name, @PathParam("str") @Context String str) { + } + } + + public static class UnknownAnnotatedConstructor { + public UnknownAnnotatedConstructor(@Context Integer name, @MatrixParam("matrix") String str) { + } + } +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyProxyResolverTest.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyProxyResolverTest.java new file mode 100644 index 0000000000..76fcd88e6f --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/injector/JerseyProxyResolverTest.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.injector; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.context.RequestScoped; +import javax.inject.Singleton; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Context; +import javax.ws.rs.ext.Providers; + +import org.glassfish.jersey.internal.JaxrsProviders; +import org.glassfish.jersey.internal.inject.Injectee; +import org.glassfish.jersey.internal.inject.InjecteeImpl; +import org.glassfish.jersey.internal.inject.InjectionResolver; + +import org.junit.Test; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * Tests Jersey Proxy Resolver. + */ +public class JerseyProxyResolverTest { + + private static Field[] FIELDS = StubForFields.class.getDeclaredFields(); + + @Test + public void testSignletonIsProxiable() { + InjecteeImpl injectee = new InjecteeImpl(); + injectee.setInjecteeClass(TestSingleton.class); + injectee.setParentClassScope(Singleton.class); + + JerseyProxyResolver resolver = new JerseyProxyResolver(); + assertTrue(resolver.isProxiable(injectee)); + } + + @Test + public void testApplicationScopeIsProxiable() { + InjecteeImpl injectee = new InjecteeImpl(); + injectee.setInjecteeClass(TestApplicationScope.class); + injectee.setParentClassScope(ApplicationScoped.class); + + JerseyProxyResolver resolver = new JerseyProxyResolver(); + assertTrue(resolver.isProxiable(injectee)); + } + + @Test + public void testRequestScopeFromNonAnnotatedIsNotProxiable() { + InjecteeImpl injectee = new InjecteeImpl(); + injectee.setInjecteeClass(TestNonAnnotatedRequestScope.class); + injectee.setParentClassScope(RequestScoped.class); + + JerseyProxyResolver resolver = new JerseyProxyResolver(); + assertFalse(resolver.isProxiable(injectee)); + } + + @Test + public void testRequestScopeIsNotProxiable() { + InjecteeImpl injectee = new InjecteeImpl(); + injectee.setInjecteeClass(TestRequestScope.class); + injectee.setParentClassScope(RequestScoped.class); + + JerseyProxyResolver resolver = new JerseyProxyResolver(); + assertFalse(resolver.isProxiable(injectee)); + } + + @Test + public void testApplicationIsNotProxiable() { + InjecteeImpl injectee = new InjecteeImpl(); + injectee.setRequiredType(Application.class); + + JerseyProxyResolver resolver = new JerseyProxyResolver(); + assertFalse(resolver.isProxiable(injectee)); + } + + @Test + public void testProxyCreated() { + MyInjectionResolver injectionResolver = new MyInjectionResolver(new JaxrsProviders()); + InjecteeImpl injectee = new InjecteeImpl(); + injectee.setRequiredType(Providers.class); + injectee.setParent(FIELDS[0]); + + JerseyProxyResolver resolver = new JerseyProxyResolver(); + Object proxy = resolver.proxy(injectee, injectionResolver); + assertTrue(proxy.getClass().getName().contains("Proxy")); + } + + @Test + public void testProxyCached() { + MyInjectionResolver injectionResolver = new MyInjectionResolver(new JaxrsProviders()); + InjecteeImpl injectee1 = new InjecteeImpl(); + injectee1.setRequiredType(Providers.class); + injectee1.setParent(FIELDS[0]); + + InjecteeImpl injectee2 = new InjecteeImpl(); + injectee2.setRequiredType(Providers.class); + injectee2.setParent(FIELDS[1]); + + JerseyProxyResolver resolver = new JerseyProxyResolver(); + Object proxy1 = resolver.proxy(injectee1, injectionResolver); + Object proxy2 = resolver.proxy(injectee2, injectionResolver); + assertSame(proxy1.getClass(), proxy2.getClass()); + } + + @Test + public void testProxyCacheNotMismatched() { + MyInjectionResolver injectionResolver1 = new MyInjectionResolver(new JaxrsProviders()); + InjecteeImpl injectee1 = new InjecteeImpl(); + injectee1.setRequiredType(Providers.class); + injectee1.setParent(FIELDS[0]); + + MyInjectionResolver injectionResolver2 = new MyInjectionResolver(new ArrayList<>()); + InjecteeImpl injectee2 = new InjecteeImpl(); + injectee2.setRequiredType(List.class); + injectee2.setParent(FIELDS[1]); + + JerseyProxyResolver resolver = new JerseyProxyResolver(); + Object proxy1 = resolver.proxy(injectee1, injectionResolver1); + Object proxy2 = resolver.proxy(injectee2, injectionResolver2); + assertNotSame(proxy1.getClass(), proxy2.getClass()); + } + + private static class StubForFields { + private Object field1; + private Object field2; + } + + private static class MyInjectionResolver implements InjectionResolver { + + private final Object instance; + + private MyInjectionResolver(Object instance) { + this.instance = instance; + } + + @Override + public Object resolve(Injectee injectee) { + return instance; + } + + @Override + public boolean isConstructorParameterIndicator() { + return true; + } + + @Override + public boolean isMethodParameterIndicator() { + return false; + } + + @Override + public Class getAnnotation() { + return Context.class; + } + } + + private static class TestNonAnnotatedRequestScope { + } + + @RequestScoped + private static class TestRequestScope { + } + + @Singleton + private static class TestSingleton { + } + + @ApplicationScoped + private static class TestApplicationScope { + } +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/BindingTestHelper.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/BindingTestHelper.java new file mode 100644 index 0000000000..f6184ebe0c --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/BindingTestHelper.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.Injections; + +import java.util.function.Consumer; + +/** + * Helper class to minimize the code in tested classes. + * + * @author Petr Bouda + */ +class BindingTestHelper { + + /** + * Accepts the provided consumer to created and register the binder. + * + * @param injectionManager injection manager which accepts the consumer. + * @param bindConsumer consumer to populate a binder. + */ + static void bind(InjectionManager injectionManager, Consumer bindConsumer) { + AbstractBinder binder = new AbstractBinder() { + @Override + protected void configure() { + bindConsumer.accept(this); + } + }; + + injectionManager.register(binder); + // injectionManager.completeRegistration(); + } + + /** + * Creates a new {@link InjectionManager}. + * + * @return newly created {@code InjectionManager}. + */ + static InjectionManager createInjectionManager() { + return Injections.createInjectionManager(); + } +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ClientInstanceInjectionTest.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ClientInstanceInjectionTest.java new file mode 100644 index 0000000000..37ec79f53e --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ClientInstanceInjectionTest.java @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import javax.enterprise.context.Dependent; +import javax.enterprise.inject.Vetoed; +import javax.inject.Inject; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.inject.weld.managed.CdiInjectionManagerFactory; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.junit.Assert; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + +public class ClientInstanceInjectionTest extends TestParent { + + @Test + public void testInject() { + InjectionManager clientInjectionManager1 = new CdiInjectionManagerFactory().create(null, RuntimeType.CLIENT); + InjectionManager clientInjectionManager2 = new CdiInjectionManagerFactory().create(null, RuntimeType.CLIENT); + + BindingTestHelper.bind(injectionManager, binder -> { + binder.bind(new StringInjectable(5)).to(Injectable.class); + }); + + BindingTestHelper.bind(clientInjectionManager1, binder -> { + binder.bind(new StringInjectable(10)).to(Injectable.class); + }); + + BindingTestHelper.bind(clientInjectionManager2, binder -> { + binder.bind(new StringInjectable(15)).to(Injectable.class); + }); + + InjectedBean bean = injectionManager.getInstance(InjectedBean.class); + Assert.assertNotNull(bean); + Assert.assertEquals("6", bean.get()); + + InjectedBean bean1 = clientInjectionManager1.getInstance(InjectedBean.class); + Assert.assertNotNull(bean1); + Assert.assertEquals("11", bean1.get()); + + InjectedBean bean2 = clientInjectionManager2.getInstance(InjectedBean.class); + Assert.assertNotNull(bean2); + Assert.assertEquals("16", bean2.get()); + + injectionManager.shutdown(); + clientInjectionManager1.shutdown(); + clientInjectionManager2.shutdown(); + } + + @Vetoed + static class InjectedBean { + @Inject + private Injectable injectable; + + public String get() { + return injectable.get(); + } + } + + static class StringInjectable extends AbstractStringInjectable implements Injectable { + StringInjectable(int i) { + super(i); + } + } + + static interface Injectable { + String get(); + } + + + @Test + public void testSupplierInject() { + InjectionManager clientInjectionManager1 = new CdiInjectionManagerFactory().create(null, RuntimeType.CLIENT); + InjectionManager clientInjectionManager2 = new CdiInjectionManagerFactory().create(null, RuntimeType.CLIENT); + + BindingTestHelper.bind(injectionManager, binder -> { + binder.bindFactory(new StringInjectableSupplier2(5)).to(Injectable2.class); + }); + + BindingTestHelper.bind(clientInjectionManager1, binder -> { + binder.bindFactory(new StringInjectableSupplier2(10)).to(Injectable2.class); + }); + + BindingTestHelper.bind(clientInjectionManager2, binder -> { + binder.bindFactory(new StringInjectableSupplier2(15)).to(Injectable2.class); + }); + + InjectedSupplierBean bean = injectionManager.getInstance(InjectedSupplierBean.class); + Assert.assertNotNull(bean); + Assert.assertEquals("6", bean.get()); + + InjectedSupplierBean bean1 = clientInjectionManager1.getInstance(InjectedSupplierBean.class); + Assert.assertNotNull(bean1); + Assert.assertEquals("11", bean1.get()); + + InjectedSupplierBean bean2 = clientInjectionManager2.getInstance(InjectedSupplierBean.class); + Assert.assertNotNull(bean2); + Assert.assertEquals("16", bean2.get()); + } + + @Dependent + static class InjectedSupplierBean { + @Inject + private Supplier injectable; + + public String get() { + return injectable.get().get(); + } + } + + static class StringInjectableSupplier2 implements Supplier { + private final int i; + StringInjectableSupplier2(int i) { + this.i = i; + } + + @Override + public Injectable2 get() { + return new StringInjectable2(i); + } + } + + private static class StringInjectable2 extends AbstractStringInjectable implements Injectable2 { + + StringInjectable2(int value) { + super(value); + } + } + + static interface Injectable2 { + String get(); + } + + abstract static class AbstractStringInjectable { + protected AtomicInteger atomicInteger = new AtomicInteger(0); + + AbstractStringInjectable(int value) { + atomicInteger.set(value); + } + + public String get() { + return String.valueOf(atomicInteger.incrementAndGet()); + } + } + + @Test + public void testSupplierClassBeanOnClientAndServer() { + InjectionManager clientInjectionManager = new CdiInjectionManagerFactory().create(null, RuntimeType.CLIENT); + BindingTestHelper.bind(injectionManager, binder -> { + binder.bindFactory(InjectableClientServerSupplierServer.class).to(InjectableClientServer.class); + }); + + BindingTestHelper.bind(clientInjectionManager, binder -> { + binder.bindFactory(InjectableClientServerSupplierClient.class).to(InjectableClientServer.class); + }); + + InjectedClientServerSupplierBean beanServer = injectionManager.getInstance(InjectedClientServerSupplierBean.class); + Assert.assertNotNull(beanServer); + Assert.assertEquals("SERVER", beanServer.get()); + + InjectedClientServerSupplierBean beanClient = clientInjectionManager.getInstance(InjectedClientServerSupplierBean.class); + Assert.assertNotNull(beanClient); + Assert.assertEquals("CLIENT", beanClient.get()); + } + + @Dependent + static class InjectedClientServerSupplierBean { + @Inject + private Supplier injectable; + + public String get() { + return injectable.get().get(); + } + } + + static class InjectableClientServerSupplierServer implements Supplier { + @Override + public InjectableClientServer get() { + return new InjectableClientServerServer(); + } + } + + static class InjectableClientServerSupplierClient implements Supplier { + @Override + public InjectableClientServer get() { + return new InjectableClientServerClient(); + } + } + + private static class InjectableClientServerServer implements InjectableClientServer { + + @Override + public String get() { + return "SERVER"; + } + } + + private static class InjectableClientServerClient implements InjectableClientServer { + @Override + public String get() { + return "CLIENT"; + } + } + + static interface InjectableClientServer { + String get(); + } +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/Conversation.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/Conversation.java new file mode 100644 index 0000000000..d10dc814e1 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/Conversation.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import java.util.function.Supplier; + +import javax.enterprise.inject.Vetoed; +import javax.inject.Inject; + +/** + * @author Petr Bouda + */ +@Vetoed +class Conversation { + + @Inject + Greeting greeting; + + @Inject + Supplier greetingSupplier; + +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechConversation.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechConversation.java new file mode 100644 index 0000000000..b3a85940f6 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechConversation.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import java.util.function.Supplier; + +import javax.enterprise.inject.Vetoed; +import javax.inject.Inject; + +/** + * @author Petr Bouda + */ +@Vetoed +class CzechConversation { + + @Inject + CzechGreeting greeting; + + @Inject + Supplier greetingSupplier; + +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechGreeting.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechGreeting.java new file mode 100644 index 0000000000..a60f5468c0 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/CzechGreeting.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import javax.enterprise.inject.Vetoed; + +/** + * @author Petr Bouda + */ +@Vetoed +public class CzechGreeting implements Greeting, Printable { + + static final String GREETING = "Ahoj"; + + @Override + public String getGreeting() { + return GREETING + "#" + Thread.currentThread().getName(); + } + + @Override + public void print() { + System.out.println(GREETING); + } + + @Override + public String toString() { + return "CzechGreeting"; + } +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/DisposableSupplierTest.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/DisposableSupplierTest.java new file mode 100644 index 0000000000..4ba137b8b6 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/DisposableSupplierTest.java @@ -0,0 +1,595 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import java.lang.reflect.Type; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +import javax.enterprise.inject.Vetoed; +import javax.inject.Inject; +import javax.ws.rs.core.GenericType; + +import org.glassfish.jersey.inject.weld.internal.bean.BeanHelper; +import org.glassfish.jersey.internal.inject.DisposableSupplier; +import org.glassfish.jersey.process.internal.RequestScope; + +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +/** + * Tests that {@link DisposableSupplier} is properly processed by {@link BeanHelper}. + * + * @author Petr Bouda + */ +@Vetoed +public class DisposableSupplierTest extends TestParent { + + private static final Type DISPOSABLE_SUPPLIER_CLASS_TYPE = + new GenericType>() {}.getType(); + private static final Type DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE = + new GenericType>() {}.getType(); + private static final Type DISPOSABLE_SUPPLIER_INSTANCE_TYPE = + new GenericType>() {}.getType(); + private static final Type PROXIABLE_DISPOSABLE_SUPPLIER_CLASS_TYPE = + new GenericType>() {}.getType(); + + private static AtomicBoolean onlyOnceGuard = new AtomicBoolean(false); + + @Before + public void bindInit() { + if (!onlyOnceGuard.getAndSet(true)) { + BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(new DisposableSupplierImpl()) + .to(StringForSupplierInstance.class)); + } + } + + @Test + public void testBindSingletonClassDisposableSupplier() { +// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(DisposableSupplierImpl.class, Singleton.class) +// .to(StringForSupplierSingletonClass.class)); + + Object supplier = injectionManager.getInstance(new GenericType>() {}.getType()); + Object disposableSupplier = injectionManager.getInstance(DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE); + assertNotNull(supplier); + assertNotNull(disposableSupplier); + assertSame(supplier, disposableSupplier); + } + + @Test + public void testBindPerLookupClassDisposableSupplier() { +// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(DisposableSupplierImpl.class) +// .to(StringForSupplierClass.class)); + + Object supplier = injectionManager.getInstance(new GenericType>() {}.getType()); + Object disposableSupplier = injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE); + assertNotNull(supplier); + assertNotNull(disposableSupplier); + assertNotSame(supplier, disposableSupplier); + } + + @Test + public void testBindInstanceDisposableSupplier() { +// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(new DisposableSupplierImpl()) +// .to(StringForSupplierInstance.class)); + + Object supplier = injectionManager.getInstance(new GenericType>() {}.getType()); + Object disposableSupplier = injectionManager.getInstance(DISPOSABLE_SUPPLIER_INSTANCE_TYPE); + assertNotNull(supplier); + assertNotNull(disposableSupplier); + assertSame(supplier, disposableSupplier); + } + + @Test + public void testNotBindClassDisposableSupplier() { +// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(SupplierGreeting.class).to(GreetingsClass.class)); + assertNull(injectionManager.getInstance(new GenericType>() {}.getType())); + } + + @Test + public void testNotBindInstanceDisposableSupplier() { +// BindingTestHelper.bind(injectionManager, +// binder -> binder.bindFactory(new SupplierGreeting()).to(GreetingsInstance.class)); + assertNull(injectionManager.getInstance(new GenericType>() {}.getType())); + } + + @Test + public void testOnlyIncrementSingletonSupplier() { +// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(DisposableSupplierImpl.class, Singleton.class) +// .to(StringForSupplierSingletonClass.class)); + + Object instance1 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE); + assertEquals("1", ((DisposableSupplier) instance1).get()); + Object instance2 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE); + assertEquals("2", ((DisposableSupplier) instance2).get()); + Object instance3 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE); + assertEquals("3", ((DisposableSupplier) instance3).get()); + dispose(instance1, instance2, instance3); + } + + @Test + public void testOnlyIncrementInstanceSupplier() { +// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(new DisposableSupplierImpl()) +// .to(StringForSupplierInstance.class)); + + Object instance1 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_INSTANCE_TYPE); + assertEquals("1", ((DisposableSupplier) instance1).get()); + Object instance2 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_INSTANCE_TYPE); + assertEquals("2", ((DisposableSupplier) instance2).get()); + Object instance3 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_INSTANCE_TYPE); + assertEquals("3", ((DisposableSupplier) instance3).get()); + dispose(instance1, instance2, instance3); + } + + @Test + public void testOnlyIncrementPerLookupSupplier() { +// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(DisposableSupplierImpl.class) +// .to(StringForSupplierClass.class)); + + Object instance1 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE); + assertEquals("1", ((DisposableSupplier) instance1).get()); + Object instance2 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE); + assertEquals("1", ((DisposableSupplier) instance2).get()); + Object instance3 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE); + assertEquals("1", ((DisposableSupplier) instance3).get()); + dispose(instance1, instance2, instance3); + } + + @Test + public void testOnlyIncrementSingletonInstances() { +// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(DisposableSupplierImpl.class, Singleton.class) +// .to(StringForSupplierSingletonClass.class)); + + Object instance1 = injectionManager.getInstance(StringForSupplierSingletonClass.class); + assertEquals("1", instance1); + Object instance2 = injectionManager.getInstance(StringForSupplierSingletonClass.class); + assertEquals("2", instance2); + Object instance3 = injectionManager.getInstance(StringForSupplierSingletonClass.class); + assertEquals("3", instance3); + Object o = injectionManager.getInstance( + new GenericType>() {}.getType()); + dispose(o, o, o); + } + + @Test + public void testOnlyIncrementInstanceInstance() { +// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(new DisposableSupplierImpl()) +// .to(StringForSupplierInstance.class)); + + Object instance1 = injectionManager.getInstance(StringForSupplierInstance.class); + assertEquals("1", instance1); + Object instance2 = injectionManager.getInstance(StringForSupplierInstance.class); + assertEquals("2", instance2); + Object instance3 = injectionManager.getInstance(StringForSupplierInstance.class); + assertEquals("3", instance3); + Object o = injectionManager.getInstance(new GenericType>() {}.getType()); + dispose(o, o, o); + } + + @Test + public void testDisposeSingletonSupplier() { +// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(DisposableSupplierImpl.class, Singleton.class) +// .to(StringForSupplierSingletonClass.class)); + + // 1-1 + DisposableSupplier supplier1 = + injectionManager.getInstance(DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE); + CharSequence instance1 = supplier1.get(); + // 2-2 + DisposableSupplier supplier2 = + injectionManager.getInstance(DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE); + CharSequence instance2 = supplier2.get(); + // 3-3 + DisposableSupplier supplier3 = + injectionManager.getInstance(DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE); + supplier3.get(); + // 2-2 + supplier1.dispose(null); + // 1-1 + supplier2.dispose(null); + // 2-2 + Supplier supplier4 = injectionManager.getInstance( + DISPOSABLE_SUPPLIER_SINGLETON_CLASS_TYPE); + CharSequence result = supplier4.get(); + assertEquals("2", result); + dispose(supplier3, supplier4); + } + + @Test + public void testDisposePerLookupSupplier() { +// BindingTestHelper.bind(injectionManager, binder -> binder.bindFactory(DisposableSupplierImpl.class) +// .to(StringForSupplierClass.class)); + + // 1 + DisposableSupplier supplier1 = + injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE); + CharSequence instance1 = supplier1.get(); + // 1 + DisposableSupplier supplier2 = + injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE); + CharSequence instance2 = supplier2.get(); + // 1 + DisposableSupplier supplier3 = + injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE); + supplier3.get(); + // 0 + supplier1.dispose(null); + // 0 + supplier2.dispose(null); + // 1 + Supplier supplier4 = injectionManager.getInstance(DISPOSABLE_SUPPLIER_CLASS_TYPE); + CharSequence result = supplier4.get(); + assertEquals("1", result); + dispose(supplier3, supplier4); + } + + @Test + public void testDisposeSingletonSupplierRequestScopedInstance() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(ProxiableDisposableSingletonSupplierImpl.class, Singleton.class) +// .to(ProxiableHolderSingletonClass.class) +// .in(RequestScoped.class); +// }); + + RequestScope request = injectionManager.getInstance(RequestScope.class); + AtomicReference> atomicSupplier = new AtomicReference<>(); + request.runInScope(() -> { + // Save Singleton Supplier for later check that the instance was disposed. + Supplier supplier = injectionManager.getInstance( + new GenericType>() {}.getType()); + atomicSupplier.set(supplier); + + // All instances should be the same because they are request scoped. + ProxiableHolderSingletonClass instance1 = injectionManager.getInstance(ProxiableHolderSingletonClass.class); + assertEquals("1", instance1.getValue()); + ProxiableHolderSingletonClass instance2 = injectionManager.getInstance(ProxiableHolderSingletonClass.class); + assertEquals("1", instance2.getValue()); + }); + + Supplier cleanedSupplier = atomicSupplier.get(); + // Next should be 1-1 + assertEquals("1", cleanedSupplier.get().getValue()); + } + + /** + * Tests that object created in request scope is disposing at the time of ending the scope. + */ + @Test + public void testDisposePerLookupSupplierRequestScopedInstance() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(ProxiableDisposableSupplierImpl.class) +// .to(ProxiableHolderClass.class) +// .in(RequestScoped.class); +// }); + + RequestScope request = injectionManager.getInstance(RequestScope.class); + AtomicReference> atomicSupplier = new AtomicReference<>(); + request.runInScope(() -> { + // Save Singleton Supplier for later check that the instance was disposed. + Supplier supplier = injectionManager.getInstance(PROXIABLE_DISPOSABLE_SUPPLIER_CLASS_TYPE); + atomicSupplier.set(supplier); + + // All instances should be the same because they are request scoped. + ProxiableHolderClass instance1 = injectionManager.getInstance(ProxiableHolderClass.class); + assertEquals("1", instance1.getValue()); + ProxiableHolderClass instance2 = injectionManager.getInstance(ProxiableHolderClass.class); + assertEquals("1", instance2.getValue()); + }); + + Supplier cleanedSupplier = atomicSupplier.get(); + // Next should be 1 + assertEquals("1", cleanedSupplier.get().getValue()); + } + + /** + * Tests that inherited request scoped is also cleaned by disposing the objects. + */ + @Test + public void testDisposeSingletonSupplierMultiRequestScoped() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(ProxiableDisposableSupplierImpl.class) +// .to(ProxiableHolderClass.class) +// .in(RequestScoped.class); +// }); + + RequestScope request = injectionManager.getInstance(RequestScope.class); + AtomicReference> firstSupplier = new AtomicReference<>(); + AtomicReference> secondSupplier = new AtomicReference<>(); + + request.runInScope(() -> { + Supplier supplier1 = injectionManager.getInstance(PROXIABLE_DISPOSABLE_SUPPLIER_CLASS_TYPE); + firstSupplier.set(supplier1); + + ProxiableHolderClass instance1 = injectionManager.getInstance(ProxiableHolderClass.class); + assertEquals("1", instance1.getValue()); + + request.runInScope(() -> { + // Save Singleton Supplier for later check that the instance was disposed. + Supplier supplier2 = injectionManager.getInstance(PROXIABLE_DISPOSABLE_SUPPLIER_CLASS_TYPE); + secondSupplier.set(supplier2); + + ProxiableHolderClass instance2 = injectionManager.getInstance(ProxiableHolderClass.class); + // 1-2 because the same static class is used in inherited runInScope + assertEquals("1", instance2.getValue()); + }); + }); + + Supplier cleanedSupplier1 = firstSupplier.get(); + Supplier cleanedSupplier2 = secondSupplier.get(); + // Next should be 1-1 + assertEquals("1", cleanedSupplier1.get().getValue()); + // 1-2 because the same static class is used but the instance is cleaned. + assertEquals("1", cleanedSupplier2.get().getValue()); + } + + /** + * PerLookup fields are not disposed therefore they should never be used as a DisposedSupplier because the field stay in + * {@link org.glassfish.jersey.inject.weld.bean.SupplierClassBean} forever. + */ + @Test + public void testDisposeComposedObjectWithPerLookupFields() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(DisposableSupplierForComposedImpl.class, Singleton.class) +// .to(StringForComposed.class); +// +// binder.bindAsContract(ComposedObject.class) +// .in(RequestScoped.class); +// }); + + RequestScope request = injectionManager.getInstance(RequestScope.class); + AtomicReference> atomicSupplier = new AtomicReference<>(); + request.runInScope(() -> { + // Save Singleton Supplier for later check that the instance was disposed. + Supplier supplier = injectionManager.getInstance( + new GenericType>() {}.getType()); + atomicSupplier.set(supplier); + + // All instances should be the same because they are request scoped. + ComposedObject instance = injectionManager.getInstance(ComposedObject.class); + assertEquals("1", instance.getFirst().toString()); + assertEquals("2", instance.getSecond().toString()); + assertEquals("3", instance.getThird().toString()); + }); + + Supplier cleanedSupplier = atomicSupplier.get(); + // Next should be 1 - all instances are disposed and decremented back + assertEquals("1", cleanedSupplier.get().toString()); + } + + private void dispose(Object... objects) { + for (Object object : objects) { + if (DisposableSupplier.class.isInstance(object)) { + ((DisposableSupplier) object).dispose(null); + } + } + } + + @Vetoed + static class ComposedObject { + + @Inject + StringForComposed first; + + @Inject + StringForComposed second; + + @Inject + StringForComposed third; + + public StringForComposed getFirst() { + return first; + } + + public StringForComposed getSecond() { + return second; + } + + public StringForComposed getThird() { + return third; + } + } + + @Vetoed + static class DisposableSupplierForComposedImpl implements DisposableSupplier { + private final AtomicInteger counter = new AtomicInteger(); + + @Override + public StringForComposed get() { + // Create a new string - don't share the instances in the string pool. + return new StringForComposed(counter.incrementAndGet() + ""); + } + + @Override + public void dispose(final StringForComposed instance) { + counter.decrementAndGet(); + } + } + + @Vetoed + static class DisposableSupplierImpl implements DisposableSupplier { + private final AtomicInteger counter = new AtomicInteger(); + + @Override + public String get() { + // Create a new string - don't share the instances in the string pool. + return new String(counter.incrementAndGet() + ""); + } + + @Override + public void dispose(final String instance) { + counter.decrementAndGet(); + } + } + + @Vetoed + static class ProxiableDisposableSupplierImpl implements DisposableSupplier { + private final AtomicInteger counter = new AtomicInteger(); + + @Override + public AbstractProxiableHolder get() { + // Create a new string - don't share the instances in the string pool. + return new ProxiableHolderClass(counter.incrementAndGet() + ""); + } + + @Override + public void dispose(AbstractProxiableHolder instance) { + counter.decrementAndGet(); + } + } + + @Vetoed + static class ProxiableDisposableSingletonSupplierImpl implements DisposableSupplier { + private final AtomicInteger counter = new AtomicInteger(); + + @Override + public AbstractProxiableHolder get() { + // Create a new string - don't share the instances in the string pool. + return new ProxiableHolderSingletonClass(counter.incrementAndGet() + ""); + } + + @Override + public void dispose(AbstractProxiableHolder instance) { + counter.decrementAndGet(); + } + } + + @Vetoed + static class ProxiableHolderSingletonClass extends AbstractProxiableHolder { + public ProxiableHolderSingletonClass(String value) { + super(value); + } + } + + @Vetoed + static class ProxiableHolderClass extends AbstractProxiableHolder { + public ProxiableHolderClass(String value) { + super(value); + } + } + + @Vetoed + abstract static class AbstractProxiableHolder { + private String value; + + public AbstractProxiableHolder(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + + @Vetoed + static final class GreetingsInstance extends ExtendableString { + public GreetingsInstance(CharSequence inner) { + super(inner); + } + } + + @Vetoed + static final class GreetingsClass extends ExtendableString { + public GreetingsClass(CharSequence inner) { + super(inner); + } + } + + @Vetoed + static final class StringForComposed extends ExtendableString { + public StringForComposed(CharSequence inner) { + super(inner); + } + } + + @Vetoed + static final class StringForSupplierSingletonClass extends ExtendableString { + public StringForSupplierSingletonClass(CharSequence inner) { + super(inner); + } + } + + @Vetoed + static final class StringForSupplierClass extends ExtendableString { + public StringForSupplierClass(CharSequence inner) { + super(inner); + } + } + + @Vetoed + static final class StringForSupplierInstance extends ExtendableString { + public StringForSupplierInstance(CharSequence inner) { + super(inner); + } + } + + static class ExtendableString implements CharSequence, Comparable { + + private final CharSequence inner; + + protected ExtendableString(CharSequence inner) { + this.inner = inner; + } + + @Override + public int length() { + return inner.length(); + } + + @Override + public char charAt(int index) { + return inner.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return inner.subSequence(start, end); + } + + @Override + public String toString() { + return inner.toString(); + } + + @Override + public int compareTo(ExtendableString o) { + if (this == o) return 0; + if (o == null) return -1; + return Objects.compare(inner.toString(), o.toString(), String.CASE_INSENSITIVE_ORDER); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + return Objects.equals(inner.toString(), inner.toString()); + } + + @Override + public int hashCode() { + return Objects.hash(inner); + } + } +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/EnglishGreeting.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/EnglishGreeting.java new file mode 100644 index 0000000000..767b1170f1 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/EnglishGreeting.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import javax.enterprise.inject.Vetoed; + +/** + * @author Petr Bouda + */ +@Vetoed +public class EnglishGreeting implements Greeting, Printable { + + static final String GREETING = "Hello"; + + @Override + public String getGreeting() { + return GREETING + "#" + Thread.currentThread().getName(); + } + + @Override + public void print() { + System.out.println(GREETING); + } + + @Override + public String toString() { + return "EnglishGreeting"; + } +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/Greeting.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/Greeting.java new file mode 100644 index 0000000000..5f0551ecf7 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/Greeting.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +/** + * @author Petr Bouda + */ +@FunctionalInterface +public interface Greeting { + + /** + * Returns greeting in a specific language. + * + * @return type of the greeting. + */ + String getGreeting(); + +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/InjectionManagerTest.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/InjectionManagerTest.java new file mode 100644 index 0000000000..6cf1053604 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/InjectionManagerTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import javax.enterprise.context.Dependent; +import javax.inject.Inject; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.inject.weld.managed.CdiInjectionManagerFactory; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.junit.Assert; +import org.junit.Test; + +public class InjectionManagerTest extends TestParent { + + @Test + public void injectionManagerTest() { + injectionManager.completeRegistration(); + final InjectionManagerInjectedBean bean = injectionManager.getInstance(InjectionManagerInjectedBean.class); + Assert.assertNotNull(bean); + InjectionManager got = bean.getInjectionManager(); + Assert.assertEquals(injectionManager, got); + + final InjectionManager clientInjectionManager = new CdiInjectionManagerFactory().create(null, RuntimeType.CLIENT); + clientInjectionManager.completeRegistration(); + final InjectionManagerInjectedBean clientBean = clientInjectionManager.getInstance(InjectionManagerInjectedBean.class); + Assert.assertNotNull(clientBean); + InjectionManager gotClient = clientBean.getInjectionManager(); + Assert.assertEquals(clientInjectionManager, gotClient); + } + + @Dependent + public static class InjectionManagerInjectedBean { + @Inject + InjectionManager injectionManager; + + InjectionManager getInjectionManager() { + return injectionManager; + } + } + +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/MyVetoedLongSupplier.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/MyVetoedLongSupplier.java new file mode 100644 index 0000000000..fe1f720be7 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/MyVetoedLongSupplier.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; + +import javax.enterprise.inject.Vetoed; + +@Vetoed +public class MyVetoedLongSupplier implements Supplier { + + private final AtomicLong counter = new AtomicLong(); + + @Override + public Long get() { + return counter.incrementAndGet(); + } +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/Printable.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/Printable.java new file mode 100644 index 0000000000..4307ea810e --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/Printable.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +/** + * @author Petr Bouda + */ +@FunctionalInterface +public interface Printable { + + void print(); + +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/PrintableConversation.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/PrintableConversation.java new file mode 100644 index 0000000000..f507e5586b --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/PrintableConversation.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import java.util.function.Supplier; + +import javax.enterprise.inject.Vetoed; +import javax.inject.Inject; + +/** + * @author Petr Bouda + */ +@Vetoed +class PrintableConversation { + + @Inject + Greeting greeting; + + @Inject + Supplier greetingSupplier; + + @Inject + Printable printable; + + @Inject + Supplier printableSupplier; + +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ProviderInjectionTest.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ProviderInjectionTest.java new file mode 100644 index 0000000000..3fe20cfbe6 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ProviderInjectionTest.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import javax.enterprise.event.Observes; +import javax.enterprise.inject.Vetoed; +import javax.enterprise.inject.se.SeContainerInitializer; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.BeforeBeanDiscovery; +import javax.enterprise.inject.spi.Extension; +import javax.inject.Inject; +import javax.inject.Provider; +import javax.ws.rs.core.Context; + +import org.glassfish.jersey.internal.inject.AbstractBinder; + +import org.hamcrest.core.StringStartsWith; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.function.Supplier; + +import static org.junit.Assert.assertThat; + +public class ProviderInjectionTest extends TestParent { + + @BeforeClass + public static void setup() { + SeContainerInitializer containerInitializer = SeContainerInitializer.newInstance(); + containerInitializer.addExtensions(new ProviderInjectionTestExtension()); + container = containerInitializer.initialize(); + } + + + @Test + public void testProviderInject() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bind(CzechGreeting.class).to(Greeting.class); +// binder.bindAsContract(ProviderInject.class); +// }); + + Greeting greeting = injectionManager.getInstance(Greeting.class); + Assert.assertNotNull(greeting); + + ProviderInject instance = injectionManager.getInstance(ProviderInject.class); + assertThat(instance.greeting.get().getGreeting(), StringStartsWith.startsWith(CzechGreeting.GREETING)); + } + + @Test + public void testProviderContext() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bind(CzechGreeting.class).to(Greeting.class); +// binder.bindAsContract(ProviderContext.class); +// }); + + ProviderContext instance = injectionManager.getInstance(ProviderContext.class); + assertThat(instance.greeting.get().getGreeting(), StringStartsWith.startsWith(CzechGreeting.GREETING)); + } + + @Test + public void testProviderFactoryInject() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(SupplierGreeting.class).to(Greeting2.class); +// binder.bindAsContract(ProviderInject2.class); +// }); + + ProviderInject2 conversation = injectionManager.getInstance(ProviderInject2.class); + assertThat(conversation.greeting.get().getGreeting(), StringStartsWith.startsWith(CzechGreeting.GREETING)); + } + + + @Test + public void testProviderFactoryContext() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(SupplierGreeting.class).to(Greeting2.class); +// binder.bindAsContract(ProviderContext2.class); +// }); + + ProviderContext2 conversation = injectionManager.getInstance(ProviderContext2.class); + assertThat(conversation.greeting.get().getGreeting(), StringStartsWith.startsWith(CzechGreeting.GREETING)); + } + + public static class ProviderInject2 { + @Inject + Provider greeting; + } + + public static class ProviderContext2 { + @Context + Provider greeting; + } + + public static class ProviderInject { + @Inject + Provider greeting; + } + + public static class ProviderContext { + @Context + Provider greeting; + } + + @Vetoed + static class SupplierGreeting implements Supplier { + + private final String greetingType; + + /** + * Default constructor. + */ + public SupplierGreeting() { + this(CzechGreeting.GREETING); + } + + /** + * Supplier's constructor. + * + * @param greetingType greetingType in a specific language. + */ + public SupplierGreeting(String greetingType) { + this.greetingType = greetingType; + } + + @Override + public Greeting2 get() { + if (CzechGreeting.GREETING.equals(greetingType)) { + return new CzechGreeting2(); + } else { + return new EnglishGreeting2(); + } + } + } + + @Vetoed + static class EnglishGreeting2 extends EnglishGreeting implements Greeting2 { + } + + @Vetoed + static class CzechGreeting2 extends CzechGreeting implements Greeting2 { + } + + @Vetoed + static class EnglishGreeting implements Greeting, Printable { + + static final String GREETING = "Hello"; + + @Override + public String getGreeting() { + return GREETING + "#" + Thread.currentThread().getName(); + } + + @Override + public void print() { + System.out.println(GREETING); + } + + @Override + public String toString() { + return "EnglishGreeting"; + } + } + + @Vetoed + static class CzechGreeting implements Greeting, Printable { + + static final String GREETING = "Ahoj"; + + @Override + public String getGreeting() { + return GREETING + "#" + Thread.currentThread().getName(); + } + + @Override + public void print() { + System.out.println(GREETING); + } + + @Override + public String toString() { + return "CzechGreeting"; + } + } + + @FunctionalInterface + interface Greeting2 { + String getGreeting(); + } + + @FunctionalInterface + interface Greeting { + String getGreeting(); + } + + @FunctionalInterface + public interface Printable { + void print(); + } + + private static class ProviderInjectionTestExtension implements Extension { + void registerBindings(@Observes BeforeBeanDiscovery beforeBeanDiscovery, BeanManager beanManager) { + AbstractBinder testBinder = new AbstractBinder() { + @Override + protected void configure() { + bind(CzechGreeting.class).to(Greeting.class); + bindAsContract(ProviderInject.class); + bindAsContract(ProviderContext.class); + + bindFactory(SupplierGreeting.class).to(Greeting2.class); + bindAsContract(ProviderInject2.class); + bindAsContract(ProviderContext2.class); + } + }; + + beanManager.getExtension(BinderRegisterExtension.class).register(beforeBeanDiscovery, testBinder.getBindings()); + } + } +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierClassBindingTest.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierClassBindingTest.java new file mode 100644 index 0000000000..bf10029457 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierClassBindingTest.java @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import java.util.function.Supplier; + +import javax.enterprise.event.Observes; +import javax.enterprise.inject.Vetoed; +import javax.enterprise.inject.se.SeContainerInitializer; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.BeforeBeanDiscovery; +import javax.enterprise.inject.spi.Extension; +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.glassfish.jersey.internal.inject.AbstractBinder; + +import org.hamcrest.core.StringStartsWith; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; + +/** + * Tests that {@link Supplier} can be registered as a class-factory. + */ +@Vetoed +public class SupplierClassBindingTest extends TestParent { + + public static final String GREET = "Hi"; + + @BeforeClass + public static void setup() { + SeContainerInitializer containerInitializer = SeContainerInitializer.newInstance(); + containerInitializer.addExtensions(new SupplierClassBindingTestExtension()); + container = containerInitializer.initialize(); + } + + @Test + public void testMessages() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(SupplierGreeting.class).to(Greeting.class); +// binder.bindAsContract(Conversation.class); +// }); + + Conversation conversation = injectionManager.getInstance(Conversation.class); + assertThat(conversation.greeting.getGreeting(), StringStartsWith.startsWith(GREET)); + } + + @Test + public void testSupplierPerLookupInstancePerLookup() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(SupplierGreeting.class).to(Greeting.class); +// binder.bindAsContract(Conversation.class); +// }); + + Conversation conversation1 = injectionManager.getInstance(Conversation.class); + Greeting greeting1 = conversation1.greeting; + Conversation conversation2 = injectionManager.getInstance(Conversation.class); + Greeting greeting2 = conversation2.greeting; + Conversation conversation3 = injectionManager.getInstance(Conversation.class); + Greeting greeting3 = conversation3.greeting; + + assertNotSame(greeting1, greeting2); + assertNotSame(greeting2, greeting3); + + Supplier supplier1 = injectionManager.getInstance(Conversation.class).greetingSupplier; + Supplier supplier2 = injectionManager.getInstance(Conversation.class).greetingSupplier; + Supplier supplier3 = injectionManager.getInstance(Conversation.class).greetingSupplier; + + assertNotSame(supplier1, supplier2); + assertNotSame(supplier2, supplier3); + } + + @Test + public void testSupplierSingletonInstancePerLookup() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(SupplierSingletonInstancePerLookup.class, Singleton.class) +// .to(GreetingSupplierSingletonInstancePerLookup.class); +// binder.bindAsContract(ConversationSupplierSingletonInstancePerLookup.class); +// }); + + GreetingSupplierSingletonInstancePerLookup greeting1 = injectionManager + .getInstance(ConversationSupplierSingletonInstancePerLookup.class).greeting; + GreetingSupplierSingletonInstancePerLookup greeting2 = injectionManager + .getInstance(ConversationSupplierSingletonInstancePerLookup.class).greeting; + GreetingSupplierSingletonInstancePerLookup greeting3 = injectionManager + .getInstance(ConversationSupplierSingletonInstancePerLookup.class).greeting; + + assertNotSame(greeting1, greeting2); + assertNotSame(greeting2, greeting3); + + Supplier supplier1 = injectionManager + .getInstance(ConversationSupplierSingletonInstancePerLookup.class).greetingSupplier; + Supplier supplier2 = injectionManager + .getInstance(ConversationSupplierSingletonInstancePerLookup.class).greetingSupplier; + Supplier supplier3 = injectionManager + .getInstance(ConversationSupplierSingletonInstancePerLookup.class).greetingSupplier; + + assertSame(supplier1, supplier2); + assertSame(supplier2, supplier3); + } + + @Test + public void testSupplierPerLookupInstanceSingleton() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(SupplierPerLookupInstanceSingleton.class) +// .to(GreetingSupplierPerLookupInstanceSingleton.class).in(Singleton.class); +// binder.bindAsContract(ConversationSupplierPerLookupInstanceSingleton.class); +// }); + + GreetingSupplierPerLookupInstanceSingleton greeting1 = injectionManager + .getInstance(ConversationSupplierPerLookupInstanceSingleton.class).greeting; + GreetingSupplierPerLookupInstanceSingleton greeting2 = injectionManager + .getInstance(ConversationSupplierPerLookupInstanceSingleton.class).greeting; + GreetingSupplierPerLookupInstanceSingleton greeting3 = injectionManager + .getInstance(ConversationSupplierPerLookupInstanceSingleton.class).greeting; + + assertSame(greeting1, greeting2); + assertSame(greeting2, greeting3); + + Supplier supplier1 = injectionManager + .getInstance(ConversationSupplierPerLookupInstanceSingleton.class).greetingSupplier; + Supplier supplier2 = injectionManager + .getInstance(ConversationSupplierPerLookupInstanceSingleton.class).greetingSupplier; + Supplier supplier3 = injectionManager + .getInstance(ConversationSupplierPerLookupInstanceSingleton.class).greetingSupplier; + + assertNotSame(supplier1, supplier2); + assertNotSame(supplier2, supplier3); + } + + @Test + public void testSupplierSingletonInstanceSingleton() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(SupplierSingletonInstanceSingleton.class, Singleton.class) +// .to(GreetingSupplierSingletonInstanceSingleton.class).in(Singleton.class); +// binder.bindAsContract(ConversationSupplierSingletonInstanceSingleton.class); +// }); + + GreetingSupplierSingletonInstanceSingleton greeting1 = injectionManager + .getInstance(ConversationSupplierSingletonInstanceSingleton.class).greeting; + GreetingSupplierSingletonInstanceSingleton greeting2 = injectionManager + .getInstance(ConversationSupplierSingletonInstanceSingleton.class).greeting; + GreetingSupplierSingletonInstanceSingleton greeting3 = injectionManager + .getInstance(ConversationSupplierSingletonInstanceSingleton.class).greeting; + + assertSame(greeting1, greeting2); + assertSame(greeting2, greeting3); + + Supplier supplier1 = injectionManager + .getInstance(ConversationSupplierSingletonInstanceSingleton.class).greetingSupplier; + Supplier supplier2 = injectionManager + .getInstance(ConversationSupplierSingletonInstanceSingleton.class).greetingSupplier; + Supplier supplier3 = injectionManager + .getInstance(ConversationSupplierSingletonInstanceSingleton.class).greetingSupplier; + + assertSame(supplier1, supplier2); + assertSame(supplier2, supplier3); + } + + @Vetoed + static class SupplierSingletonInstanceSingleton implements Supplier { + @Override + public GreetingSupplierSingletonInstanceSingleton get() { + return new GreetingSupplierSingletonInstanceSingleton() { + @Override + public String getGreeting() { + return GREET; + } + }; + } + } + + @Vetoed + static class ConversationSupplierSingletonInstanceSingleton { + @Inject + GreetingSupplierSingletonInstanceSingleton greeting; + + @Inject + Supplier greetingSupplier; + } + + @FunctionalInterface + static interface GreetingSupplierSingletonInstanceSingleton { + String getGreeting(); + } + + // --- + + @Vetoed + static class SupplierPerLookupInstanceSingleton implements Supplier { + @Override + public GreetingSupplierPerLookupInstanceSingleton get() { + return () -> GREET; + } + } + + @Vetoed + static class ConversationSupplierPerLookupInstanceSingleton { + @Inject + GreetingSupplierPerLookupInstanceSingleton greeting; + + @Inject + Supplier greetingSupplier; + } + + @FunctionalInterface + static interface GreetingSupplierPerLookupInstanceSingleton { + String getGreeting(); + } + + // --- + + @Vetoed + static class SupplierSingletonInstancePerLookup implements Supplier { + @Override + public GreetingSupplierSingletonInstancePerLookup get() { + return new GreetingSupplierSingletonInstancePerLookup() { // Using Lambda will provide singletons + @Override + public String getGreeting() { + return GREET; + } + }; + } + } + + @Vetoed + static class ConversationSupplierSingletonInstancePerLookup { + @Inject + GreetingSupplierSingletonInstancePerLookup greeting; + + @Inject + Supplier greetingSupplier; + } + + @FunctionalInterface + static interface GreetingSupplierSingletonInstancePerLookup { + String getGreeting(); + } + + // --- + + @Vetoed + static class SupplierGreeting implements Supplier { + @Override + public Greeting get() { + return new Greeting() { // Using Lambda will provide singletons + @Override + public String getGreeting() { + return GREET; + } + }; + } + } + + @Vetoed + static class Conversation { + @Inject + Greeting greeting; + + @Inject + Supplier greetingSupplier; + } + + @FunctionalInterface + static interface Greeting { + String getGreeting(); + } + + private static class SupplierClassBindingTestExtension implements Extension { + void registerBindings(@Observes BeforeBeanDiscovery beforeBeanDiscovery, BeanManager beanManager) { + AbstractBinder testBinder = new AbstractBinder() { + @Override + protected void configure() { + bindFactory(SupplierGreeting.class).to(Greeting.class); + bindAsContract(Conversation.class); + + bindFactory(SupplierSingletonInstancePerLookup.class, Singleton.class) + .to(GreetingSupplierSingletonInstancePerLookup.class); + bindAsContract(ConversationSupplierSingletonInstancePerLookup.class); + + bindFactory(SupplierPerLookupInstanceSingleton.class) + .to(GreetingSupplierPerLookupInstanceSingleton.class).in(Singleton.class); + bindAsContract(ConversationSupplierPerLookupInstanceSingleton.class); + + bindFactory(SupplierSingletonInstanceSingleton.class, Singleton.class) + .to(GreetingSupplierSingletonInstanceSingleton.class).in(Singleton.class); + bindAsContract(ConversationSupplierSingletonInstanceSingleton.class); + } + }; + + beanManager.getExtension(BinderRegisterExtension.class).register(beforeBeanDiscovery, testBinder.getBindings()); + } + } + +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierContractsTest.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierContractsTest.java new file mode 100644 index 0000000000..8f576e11d3 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierContractsTest.java @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import javax.enterprise.event.Observes; +import javax.enterprise.inject.Vetoed; +import javax.enterprise.inject.se.SeContainer; +import javax.enterprise.inject.se.SeContainerInitializer; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.BeforeBeanDiscovery; +import javax.enterprise.inject.spi.Extension; +import javax.inject.Singleton; + +import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.InjectionManager; + +import org.glassfish.jersey.internal.inject.Injections; +import org.jboss.weld.exceptions.DeploymentException; +import org.junit.After; +import org.junit.Test; + +import java.util.function.Consumer; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + +/** + * Tests that {@link java.util.function.Supplier} can contain multiple contracts. + * + * @author Petr Bouda + */ +@Vetoed +public class SupplierContractsTest { + + protected static SeContainer container; + protected InjectionManager injectionManager; + + public void setup(Consumer binding) { + Extension extension = new Extension() { + void registerBindings(@Observes BeforeBeanDiscovery beforeBeanDiscovery, BeanManager beanManager) { + AbstractBinder testBinder = new AbstractBinder() { + @Override + protected void configure() { + binding.accept(this); + } + }; + beanManager.getExtension(BinderRegisterExtension.class).register(beforeBeanDiscovery, testBinder.getBindings()); + } + }; + + SeContainerInitializer containerInitializer = SeContainerInitializer.newInstance(); + containerInitializer.addExtensions(extension); + container = containerInitializer.initialize(); + injectionManager = Injections.createInjectionManager(); + } + + @After + public void tearDown() throws Exception { + if (container != null && container.isRunning()) { + container.close(); + } + } + + @Test + public void testClassFactoryInstanceInterface() { +// setup(new SupplierContractsTestExtension() { +// @Override +// void bind(AbstractBinder binder) { +// binder.bindFactory(SupplierGreeting.class).to(Greeting.class); +// binder.bindAsContract(Conversation.class); +// } +// }); + + setup((binder) -> { + binder.bindFactory(SupplierGreeting.class).to(Greeting.class); + binder.bindAsContract(Conversation.class); + }); + + Conversation conversation = injectionManager.getInstance(Conversation.class); + assertNotNull(conversation.greeting); + assertNotNull(conversation.greetingSupplier.get()); + } + + @Test + public void testClassFactoryInstanceImplementation() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(SupplierGreeting.class).to(CzechGreeting.class); +// binder.bindAsContract(CzechConversation.class); +// }); + setup((binder) -> { + binder.bindFactory(SupplierGreeting.class).to(CzechGreeting.class); + binder.bindAsContract(CzechConversation.class); + }); + + CzechConversation conversation = injectionManager.getInstance(CzechConversation.class); + assertNotNull(conversation.greeting); + assertNotNull(conversation.greetingSupplier.get()); + } + + @Test + public void testInstanceFactoryInstanceInterface() { + setup((binder) -> { + binder.bindFactory(new SupplierGreeting()).to(Greeting.class); + binder.bindAsContract(Conversation.class); + }); + + BindingTestHelper.bind(injectionManager, binder -> { + binder.bindFactory(new SupplierGreeting()).to(Greeting.class); +// binder.bindAsContract(Conversation.class); + }); + + Conversation conversation = injectionManager.getInstance(Conversation.class); + assertNotNull(conversation.greeting); + assertNotNull(conversation.greetingSupplier.get()); + } + + @Test + public void testInstanceFactoryInstanceImplementation() { + setup((binder) -> { + binder.bindFactory(new SupplierGreeting()).to(CzechGreeting.class); + binder.bindAsContract(CzechConversation.class); + }); + + BindingTestHelper.bind(injectionManager, binder -> { + binder.bindFactory(new SupplierGreeting()).to(CzechGreeting.class); +// binder.bindAsContract(CzechConversation.class); + }); + + CzechConversation conversation = injectionManager.getInstance(CzechConversation.class); + assertNotNull(conversation.greeting); + assertNotNull(conversation.greetingSupplier.get()); + } + + @Test + public void testClassFactoryMultipleContracts() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(SupplierGreeting.class) +// .to(Greeting.class) +// .to(Printable.class); +// binder.bindAsContract(PrintableConversation.class); +// }); + + setup((binder) -> { + binder.bindFactory(SupplierGreeting.class).to(Greeting.class).to(Printable.class); + binder.bindAsContract(PrintableConversation.class); + }); + + PrintableConversation conversation = injectionManager.getInstance(PrintableConversation.class); + assertNotNull(conversation.greeting); + assertNotNull(conversation.printable); + assertNotNull(conversation.greetingSupplier); + assertNotNull(conversation.printableSupplier); + + assertNotSame(conversation.greeting, conversation.printable); + assertNotSame(conversation.greetingSupplier, conversation.printableSupplier); + } + + @Test + public void testClassFactorySingletonMultipleContracts() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(SupplierGreeting.class, Singleton.class) +// .to(Greeting.class) +// .to(Printable.class); +// binder.bindAsContract(PrintableConversation.class); +// }); + + setup((binder) -> { + binder.bindFactory(SupplierGreeting.class, Singleton.class).to(Greeting.class).to(Printable.class); + binder.bindAsContract(PrintableConversation.class); + }); + + PrintableConversation conversation = injectionManager.getInstance(PrintableConversation.class); + assertNotNull(conversation.greeting); + assertNotNull(conversation.printable); + assertNotNull(conversation.greetingSupplier); + assertNotNull(conversation.printableSupplier); + + assertNotSame(conversation.greeting, conversation.printable); + assertSame(conversation.greetingSupplier, conversation.printableSupplier); + } + + @Test + public void testClassFactoryMultipleContractsSingleton() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(SupplierGreeting.class) +// .to(Greeting.class) +// .to(Printable.class) +// .in(Singleton.class); +// binder.bindAsContract(PrintableConversation.class); +// }); + + setup((binder) -> { + binder.bindFactory(SupplierGreeting.class).to(Greeting.class).to(Printable.class).in(Singleton.class); + binder.bindAsContract(PrintableConversation.class); + }); + + PrintableConversation conversation = injectionManager.getInstance(PrintableConversation.class); + assertNotNull(conversation.greeting); + assertNotNull(conversation.printable); + assertNotNull(conversation.greetingSupplier); + assertNotNull(conversation.printableSupplier); + + assertSame(conversation.greeting, conversation.printable); + assertNotSame(conversation.greetingSupplier, conversation.printableSupplier); + } + + @Test + public void testInstanceFactoryMultipleContracts() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(new SupplierGreeting()) +// .to(Greeting.class) +// .to(Printable.class); +// binder.bindAsContract(PrintableConversation.class); +// }); + + setup((binder) -> { + binder.bindFactory(new SupplierGreeting()).to(Greeting.class).to(Printable.class); + binder.bindAsContract(PrintableConversation.class); + }); + + BindingTestHelper.bind(injectionManager, binder -> { + binder.bindFactory(new SupplierGreeting()) + .to(Greeting.class) + .to(Printable.class); +// binder.bindAsContract(PrintableConversation.class); + }); + + PrintableConversation conversation = injectionManager.getInstance(PrintableConversation.class); + assertNotNull(conversation.greeting); + assertNotNull(conversation.printable); + assertNotNull(conversation.greetingSupplier); + assertNotNull(conversation.printableSupplier); + + assertNotSame(conversation.greeting, conversation.printable); + assertSame(conversation.greetingSupplier, conversation.printableSupplier); + } + + @Test + public void testInstanceFactoryMultipleContractsSingleton() { + setup((binder) -> { + binder.bindFactory(new SupplierGreeting()).to(Greeting.class).to(Printable.class).in(Singleton.class); + binder.bindAsContract(PrintableConversation.class); + }); + + BindingTestHelper.bind(injectionManager, binder -> { + binder.bindFactory(new SupplierGreeting()) + .to(Greeting.class) + .to(Printable.class) + .in(Singleton.class); +// binder.bindAsContract(PrintableConversation.class); + }); + + PrintableConversation conversation = injectionManager.getInstance(PrintableConversation.class); + assertNotNull(conversation.greeting); + assertNotNull(conversation.printable); + assertNotNull(conversation.greetingSupplier); + assertNotNull(conversation.printableSupplier); + + assertSame(conversation.greeting, conversation.printable); + assertSame(conversation.greetingSupplier, conversation.printableSupplier); + } + + @Test(expected = DeploymentException.class) + public void testClassFactoryFailedWrongImplementation() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(SupplierGreeting.class).to(EnglishGreeting.class); +// binder.bindAsContract(Conversation.class); +// }); + + setup((binder) -> { + binder.bindFactory(SupplierGreeting.class).to(EnglishGreeting.class); + binder.bindAsContract(Conversation.class); + }); + + injectionManager.getInstance(Conversation.class); + } + + @Test(expected = DeploymentException.class) + public void testInstanceFactoryFailsWrongImplementation() { + setup((binder) -> { + binder.bindFactory(new SupplierGreeting()).to(EnglishGreeting.class); + binder.bindAsContract(Conversation.class); + }); + + BindingTestHelper.bind(injectionManager, binder -> { + binder.bindFactory(new SupplierGreeting()).to(EnglishGreeting.class); +// binder.bindAsContract(Conversation.class); + }); + + injectionManager.getInstance(Conversation.class); + } + + @Test(expected = DeploymentException.class) + public void testFailsImplementationButInterfaceExpected() { + setup((binder) -> { + binder.bindFactory(new SupplierGreeting()).to(CzechGreeting.class); + binder.bindAsContract(Conversation.class); + }); + + BindingTestHelper.bind(injectionManager, binder -> { + binder.bindFactory(new SupplierGreeting()).to(CzechGreeting.class); +// binder.bindAsContract(Conversation.class); + }); + + injectionManager.getInstance(Conversation.class); + } +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierGreeting.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierGreeting.java new file mode 100644 index 0000000000..2f971704aa --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierGreeting.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import java.util.function.Supplier; + +import javax.enterprise.inject.Vetoed; + +/** + * @author Petr Bouda + */ +@Vetoed +public class SupplierGreeting implements Supplier { + + private final String greetingType; + + /** + * Default constructor. + */ + public SupplierGreeting() { + this(CzechGreeting.GREETING); + } + + /** + * Supplier's constructor. + * + * @param greetingType greetingType in a specific language. + */ + public SupplierGreeting(String greetingType) { + this.greetingType = greetingType; + } + + @Override + public Greeting get() { + if (CzechGreeting.GREETING.equals(greetingType)) { + return new CzechGreeting(); + } else { + return new EnglishGreeting(); + } + } +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierInstanceBindingTest.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierInstanceBindingTest.java new file mode 100644 index 0000000000..eb90843edd --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/SupplierInstanceBindingTest.java @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; + +import javax.enterprise.event.Observes; +import javax.enterprise.inject.se.SeContainerInitializer; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.BeforeBeanDiscovery; +import javax.enterprise.inject.spi.Extension; +import javax.enterprise.inject.Vetoed; +import javax.inject.Inject; +import javax.ws.rs.core.GenericType; + +import org.glassfish.jersey.internal.inject.AbstractBinder; + +import org.hamcrest.core.StringStartsWith; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; + +/** + * Tests that {@link Supplier} can be registered as a instance-factory. + * + * @author Petr Bouda + */ +@Vetoed +public class SupplierInstanceBindingTest extends TestParent { + + private static long supplierHit = 2; + private static AtomicBoolean runOnlyOnceGuard = new AtomicBoolean(false); + + @BeforeClass + public static void setup() { + SeContainerInitializer containerInitializer = SeContainerInitializer.newInstance(); + containerInitializer.addExtensions(new SupplierInstanceBindingTestExtension()); + container = containerInitializer.initialize(); + } + + @Before + public void initBinding() { + if (!runOnlyOnceGuard.getAndSet(true)) { + MyVetoedLongSupplier supplier = new MyVetoedLongSupplier(); + supplier.get(); + supplier.get(); + + BindingTestHelper.bind(injectionManager, binder -> + binder.bindFactory(supplier).to(Long.class)); + + BindingTestHelper.bind(injectionManager, binder -> { + binder.bindFactory(new SupplierGreeting()).to(Greeting.class); + }); + } + } + + @Test + public void testInstanceFactorySupplierOnly() { +// MyVetoedLongSupplier supplier = new MyVetoedLongSupplier(); +// supplier.get(); +// supplier.get(); +// +// BindingTestHelper.bind(injectionManager, binder -> +// binder.bindFactory(supplier).to(Long.class)); + + Supplier instance = injectionManager.getInstance(new GenericType>() { + }.getType()); + assertEquals((Long) (supplierHit++ + 1), instance.get()); + assertEquals((Long) (supplierHit++ + 1), instance.get()); + } + + @Test + public void testInstanceFactoryValuesOnly() { +// MyVetoedLongSupplier supplier = new MyVetoedLongSupplier(); +// supplier.get(); +// supplier.get(); +// +// BindingTestHelper.bind(injectionManager, binder -> +// binder.bindFactory(supplier).to(Long.class)); + + Long instance3 = injectionManager.getInstance(Long.class); + Long instance4 = injectionManager.getInstance(Long.class); + + assertEquals((Long) (supplierHit++ + 1), instance3); + assertEquals((Long) (supplierHit++ + 1), instance4); + } + + @Test + public void testMessages() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(new SupplierGreeting()).to(Greeting.class); +// binder.bindAsContract(Conversation.class); +// }); + + Conversation conversation = injectionManager.getInstance(Conversation.class); + assertThat(conversation.greeting.getGreeting(), StringStartsWith.startsWith(CzechGreeting.GREETING)); + } + + @Test + public void testSupplierSingletonInstancePerLookup() { +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindFactory(new SupplierGreeting()).to(Greeting.class); +// binder.bindAsContract(Conversation.class); +// }); + + Greeting greeting1 = injectionManager.getInstance(Conversation.class).greeting; + Greeting greeting2 = injectionManager.getInstance(Conversation.class).greeting; + Greeting greeting3 = injectionManager.getInstance(Conversation.class).greeting; + + assertNotSame(greeting1, greeting2); + assertNotSame(greeting2, greeting3); + + Supplier supplier1 = injectionManager.getInstance(Conversation.class).greetingSupplier; + Supplier supplier2 = injectionManager.getInstance(Conversation.class).greetingSupplier; + Supplier supplier3 = injectionManager.getInstance(Conversation.class).greetingSupplier; + + assertSame(supplier1, supplier2); + assertSame(supplier2, supplier3); + } + + @Vetoed + static class Conversation { + @Inject + Greeting greeting; + + @Inject + Supplier greetingSupplier; + } + + @Vetoed + static class SupplierGreeting implements Supplier { + + private final String greetingType; + + /** + * Default constructor. + */ + public SupplierGreeting() { + this(CzechGreeting.GREETING); + } + + /** + * Supplier's constructor. + * + * @param greetingType greetingType in a specific language. + */ + public SupplierGreeting(String greetingType) { + this.greetingType = greetingType; + } + + @Override + public Greeting get() { + if (CzechGreeting.GREETING.equals(greetingType)) { + return new CzechGreeting(); + } else { + return new EnglishGreeting(); + } + } + } + + static class EnglishGreeting implements Greeting { + + static final String GREETING = "Hello"; + + @Override + public String getGreeting() { + return GREETING + "#" + Thread.currentThread().getName(); + } + + @Override + public String toString() { + return "EnglishGreeting"; + } + } + + static class CzechGreeting implements Greeting { + + static final String GREETING = "Ahoj"; + + @Override + public String getGreeting() { + return GREETING + "#" + Thread.currentThread().getName(); + } + + @Override + public String toString() { + return "CzechGreeting"; + } + } + + @FunctionalInterface + static interface Greeting { + /** + * Returns greeting in a specific language. + * + * @return type of the greeting. + */ + String getGreeting(); + } + + private static class SupplierInstanceBindingTestExtension implements Extension { + void registerBindings(@Observes BeforeBeanDiscovery beforeBeanDiscovery, BeanManager beanManager) { + AbstractBinder testBinder = new AbstractBinder() { + @Override + protected void configure() { + MyVetoedLongSupplier supplier = new MyVetoedLongSupplier(); + supplier.get(); + supplier.get(); + bindFactory(supplier).to(Long.class); + + bindFactory(new SupplierGreeting()).to(Greeting.class); + bindAsContract(Conversation.class); + } + }; + + beanManager.getExtension(BinderRegisterExtension.class).register(beforeBeanDiscovery, testBinder.getBindings()); + } + } +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestParent.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestParent.java new file mode 100644 index 0000000000..0938dd239d --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestParent.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import javax.enterprise.inject.se.SeContainer; +import javax.enterprise.inject.se.SeContainerInitializer; +import org.glassfish.jersey.internal.inject.InjectionManager; +import org.glassfish.jersey.internal.inject.Injections; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; + +public class TestParent { + protected static SeContainer container; + protected InjectionManager injectionManager; + + + @BeforeClass + public static void setup() { + SeContainerInitializer containerInitializer = SeContainerInitializer.newInstance(); + container = containerInitializer.initialize(); + } + + @Before + public void init() { + injectionManager = Injections.createInjectionManager(); + } + + @AfterClass + public static void tearDown() throws Exception { + container.close(); + } + +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestPreinitialization.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestPreinitialization.java new file mode 100644 index 0000000000..a9096b0e14 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/TestPreinitialization.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import javax.enterprise.context.Dependent; +import javax.inject.Singleton; +import javax.ws.rs.RuntimeType; + +import org.glassfish.jersey.inject.weld.spi.BootstrapPreinitialization; +import org.glassfish.jersey.internal.inject.AbstractBinder; +import org.glassfish.jersey.internal.inject.PerThread; +import org.glassfish.jersey.process.internal.RequestScoped; + +public class TestPreinitialization implements BootstrapPreinitialization { + @Override + public void register(RuntimeType runtimeType, AbstractBinder binder) { + if (RuntimeType.CLIENT == runtimeType) { + //ClientInstanceInjectionTest + { + binder.bindFactory(ClientInstanceInjectionTest.InjectableClientServerSupplierClient.class) + .to(ClientInstanceInjectionTest.InjectableClientServer.class); + } + return; + } + + // Disposable supplier test + { + binder.bindFactory(DisposableSupplierTest.DisposableSupplierImpl.class, Singleton.class) + .to(DisposableSupplierTest.StringForSupplierSingletonClass.class); + binder.bindFactory(DisposableSupplierTest.DisposableSupplierImpl.class) + .to(DisposableSupplierTest.StringForSupplierClass.class); + binder.bindFactory(new DisposableSupplierTest.DisposableSupplierImpl()) + .to(DisposableSupplierTest.StringForSupplierInstance.class); + binder.bindFactory(SupplierGreeting.class) + .to(DisposableSupplierTest.GreetingsClass.class); + binder.bindFactory(new SupplierGreeting()) + .to(DisposableSupplierTest.GreetingsInstance.class); + + binder.bindFactory(DisposableSupplierTest.ProxiableDisposableSingletonSupplierImpl.class, Singleton.class) + .to(DisposableSupplierTest.ProxiableHolderSingletonClass.class) + .in(RequestScoped.class); + binder.bindFactory(DisposableSupplierTest.ProxiableDisposableSupplierImpl.class) + .to(DisposableSupplierTest.ProxiableHolderClass.class) + .in(RequestScoped.class); + + binder.bindFactory(DisposableSupplierTest.DisposableSupplierForComposedImpl.class, Singleton.class) + .to(DisposableSupplierTest.StringForComposed.class); + binder.bindAsContract(DisposableSupplierTest.ComposedObject.class) + .in(RequestScoped.class); + } + + // ThreadScopeTest + { + //testThreadScopedInDifferentThread + binder.bindAsContract(ThreadScopeTest.SingletonObject.class) + .in(Singleton.class); + binder.bindFactory(new ThreadScopeTest.SupplierGreeting()) + .to(ThreadScopeTest.Greeting.class) + .in(PerThread.class); + + //testThreadScopedInRequestScope + binder.bindAsContract(ThreadScopeTest.RequestScopedInterface.class) + .in(javax.enterprise.context.RequestScoped.class); +// bindFactory(new SupplierGreeting()) +// .to(Greeting.class) +// .in(PerThread.class); + + //testThreadScopedInRequestScopeImplementation + binder.bindAsContract(ThreadScopeTest.RequestScopedCzech.class) + .in(javax.enterprise.context.RequestScoped.class); + binder.bindFactory(new ThreadScopeTest.SupplierGreeting()) + .to(ThreadScopeTest.CzechGreeting.class) + .in(PerThread.class); + + //testThreadScopedInRequestTwoTypes + binder.bindAsContract(ThreadScopeTest.RequestScopedCzech2.class) + .in(javax.enterprise.context.RequestScoped.class); + binder.bindAsContract(ThreadScopeTest.RequestScopedEnglish2.class) + .in(javax.enterprise.context.RequestScoped.class); + binder.bindFactory(new ThreadScopeTest.SupplierGreeting2(ThreadScopeTest.CzechGreeting2.GREETING)) + .to(ThreadScopeTest.CzechGreeting2.class) + .in(PerThread.class); + binder.bindFactory(new ThreadScopeTest.SupplierGreeting2(ThreadScopeTest.EnglishGreeting2.GREETING)) + .to(ThreadScopeTest.EnglishGreeting2.class) + .in(PerThread.class); + } + + //ClientInstanceInjectionTest + { + binder.bind(new ClientInstanceInjectionTest.StringInjectable(0)) + .to(ClientInstanceInjectionTest.Injectable.class).in(Dependent.class); + binder.bindAsContract(ClientInstanceInjectionTest.InjectedBean.class); + + binder.bindFactory(new ClientInstanceInjectionTest.StringInjectableSupplier2(0)) + .to(ClientInstanceInjectionTest.Injectable2.class).in(Dependent.class); + //bindAsContract(ClientInstanceInjectionTest.InjectedSupplierBean.class); + + binder.bindFactory(ClientInstanceInjectionTest.InjectableClientServerSupplierServer.class) + .to(ClientInstanceInjectionTest.InjectableClientServer.class); + } + } +} diff --git a/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ThreadScopeTest.java b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ThreadScopeTest.java new file mode 100644 index 0000000000..e3fc4d2864 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/java/org/glassfish/jersey/inject/weld/internal/managed/ThreadScopeTest.java @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.inject.weld.internal.managed; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; + +import javax.enterprise.context.RequestScoped; +import javax.enterprise.inject.Vetoed; +import javax.inject.Inject; +import javax.inject.Singleton; + +import org.glassfish.jersey.internal.inject.PerThread; +import org.glassfish.jersey.process.internal.RequestScope; + +import org.hamcrest.core.StringStartsWith; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertThat; + +/** + * Testing thread scope integration. + * + * @author Petr Bouda + */ +public class ThreadScopeTest extends TestParent { + + private static AtomicBoolean runOnlyOnceGuard = new AtomicBoolean(false); + + @Before + public void initOnce() { + if (!runOnlyOnceGuard.getAndSet(true)) { + BindingTestHelper.bind(injectionManager, binder -> { + binder.bindFactory(new SupplierGreeting()) + .to(Greeting.class) + .in(PerThread.class); + }); + } + } + + @Test + public void testThreadScopedInDifferentThread() throws InterruptedException { +// InjectionManager injectionManager = BindingTestHelper.createInjectionManager(); +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindAsContract(SingletonObject.class) +// .in(Singleton.class); +// +// binder.bindFactory(new SupplierGreeting()) +// .to(Greeting.class) +// .in(PerThread.class); +// }); + + SingletonObject instance1 = injectionManager.getInstance(SingletonObject.class); + Greeting greeting1 = instance1.getGreeting(); + String greetingString1 = greeting1.getGreeting(); + assertThat(greetingString1, StringStartsWith.startsWith(CzechGreeting.GREETING)); + + CountDownLatch latch = new CountDownLatch(1); + new Thread(() -> { + // Precisely the same object + SingletonObject instance2 = injectionManager.getInstance(SingletonObject.class); + Greeting greeting2 = instance2.getGreeting(); + String greetingString2 = greeting2.getGreeting(); + assertThat(greetingString2, StringStartsWith.startsWith(CzechGreeting.GREETING)); + + assertNotEquals(greetingString1, greetingString2); + latch.countDown(); + }).start(); + + latch.await(); + + SingletonObject instance3 = injectionManager.getInstance(SingletonObject.class); + assertEquals(instance3.getGreeting().getGreeting(), greetingString1); + } + + @Test + public void testThreadScopedInRequestScope() { +// InjectionManager injectionManager = BindingTestHelper.createInjectionManager(); +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindAsContract(RequestScopedInterface.class) +// .in(RequestScoped.class); +// +// binder.bindFactory(new SupplierGreeting()) +// .to(Greeting.class) +// .in(PerThread.class); +// }); + + RequestScope request = injectionManager.getInstance(RequestScope.class); + request.runInScope(() -> { + RequestScopedInterface instance1 = injectionManager.getInstance(RequestScopedInterface.class); + Greeting greeting1 = instance1.getGreeting(); + assertNotNull(greeting1); + + // Precisely the same object + RequestScopedInterface instance2 = injectionManager.getInstance(RequestScopedInterface.class); + Greeting greeting2 = instance2.getGreeting(); + assertNotNull(greeting2); + + assertEquals(greeting1, greeting2); + }); + } + + @Test + public void testThreadScopedInRequestScopeImplementation() { +// InjectionManager injectionManager = BindingTestHelper.createInjectionManager(); +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindAsContract(RequestScopedCzech.class) +// .in(RequestScoped.class); +// +// binder.bindFactory(new SupplierGreeting()) +// .to(CzechGreeting.class) +// .in(PerThread.class); +// }); + + RequestScope request = injectionManager.getInstance(RequestScope.class); + request.runInScope(() -> { + RequestScopedCzech instance1 = injectionManager.getInstance(RequestScopedCzech.class); + CzechGreeting greeting1 = instance1.getGreeting(); + assertNotNull(greeting1); + + // Precisely the same object + RequestScopedCzech instance2 = injectionManager.getInstance(RequestScopedCzech.class); + CzechGreeting greeting2 = instance2.getGreeting(); + assertNotNull(greeting2); + + assertEquals(greeting1, greeting2); + }); + } + + @Test + public void testThreadScopedInRequestTwoTypes() { +// InjectionManager injectionManager = BindingTestHelper.createInjectionManager(); +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindAsContract(RequestScopedCzech.class) +// .in(RequestScoped.class); +// +// binder.bindAsContract(RequestScopedEnglish.class) +// .in(RequestScoped.class); +// +// binder.bindFactory(new SupplierGreeting(CzechGreeting.GREETING)) +// .to(CzechGreeting.class) +// .in(PerThread.class); +// +// binder.bindFactory(new SupplierGreeting(EnglishGreeting.GREETING)) +// .to(EnglishGreeting.class) +// .in(PerThread.class); +// }); + + RequestScope request = injectionManager.getInstance(RequestScope.class); + request.runInScope(() -> { + RequestScopedCzech2 instance1 = injectionManager.getInstance(RequestScopedCzech2.class); + CzechGreeting2 greeting1 = instance1.getGreeting(); + assertNotNull(greeting1); + + // Precisely the same object + RequestScopedEnglish2 instance2 = injectionManager.getInstance(RequestScopedEnglish2.class); + EnglishGreeting2 greeting2 = instance2.getGreeting(); + assertNotNull(greeting2); + + assertNotSame(greeting1, greeting2); + }); + } + + @Test + public void testThreadScopedInSingletonScope() { +// InjectionManager injectionManager = BindingTestHelper.createInjectionManager(); +// BindingTestHelper.bind(injectionManager, binder -> { +// binder.bindAsContract(SingletonObject.class) +// .in(Singleton.class); +// +// binder.bindFactory(new SupplierGreeting()) +// .to(Greeting.class) +// .in(PerThread.class); +// }); + + SingletonObject instance1 = injectionManager.getInstance(SingletonObject.class); + Greeting greeting1 = instance1.getGreeting(); + assertNotNull(greeting1); + + // Precisely the same object + SingletonObject instance2 = injectionManager.getInstance(SingletonObject.class); + Greeting greeting2 = instance2.getGreeting(); + assertNotNull(greeting2); + + assertEquals(greeting1, greeting2); + } + + @RequestScoped + public static class RequestScopedInterface { + + @Inject + Greeting greeting; + + public Greeting getGreeting() { + return greeting; + } + } + + @RequestScoped + public static class RequestScopedCzech { + + @Inject + CzechGreeting greeting; + + public CzechGreeting getGreeting() { + return greeting; + } + } + + @Singleton + public static class SingletonObject { + + @Inject + Greeting greeting; + + public Greeting getGreeting() { + return greeting; + } + } + + @RequestScoped + public static class RequestScopedCzech2 { + + @Inject + CzechGreeting2 greeting; + + public CzechGreeting2 getGreeting() { + return greeting; + } + } + + @RequestScoped + public static class RequestScopedEnglish2 { + + @Inject + EnglishGreeting2 greeting; + + public EnglishGreeting2 getGreeting() { + return greeting; + } + } + + @Vetoed + static class SupplierGreeting2 implements Supplier { + + private final String greetingType; + + /** + * Default constructor. + */ + public SupplierGreeting2() { + this(CzechGreeting.GREETING); + } + + /** + * Supplier's constructor. + * + * @param greetingType greetingType in a specific language. + */ + public SupplierGreeting2(String greetingType) { + this.greetingType = greetingType; + } + + @Override + public Greeting2 get() { + if (CzechGreeting2.GREETING.equals(greetingType)) { + return new CzechGreeting2(); + } else { + return new EnglishGreeting2(); + } + } + } + + @Vetoed + static class CzechGreeting2 implements Greeting2 { + + static final String GREETING = "Ahoj"; + + @Override + public String getGreeting() { + return GREETING + "#" + Thread.currentThread().getName(); + } + + @Override + public String toString() { + return "CzechGreeting"; + } + } + + @Vetoed + static class EnglishGreeting2 implements Greeting2 { + + static final String GREETING = "Hello"; + + @Override + public String getGreeting() { + return GREETING + "#" + Thread.currentThread().getName(); + } + + @Override + public String toString() { + return "EnglishGreeting"; + } + } + + @Vetoed + static class SupplierGreeting implements Supplier { + + private final String greetingType; + + /** + * Default constructor. + */ + public SupplierGreeting() { + this(CzechGreeting.GREETING); + } + + /** + * Supplier's constructor. + * + * @param greetingType greetingType in a specific language. + */ + public SupplierGreeting(String greetingType) { + this.greetingType = greetingType; + } + + @Override + public Greeting get() { + if (CzechGreeting.GREETING.equals(greetingType)) { + return new CzechGreeting(); + } else { + return new EnglishGreeting(); + } + } + } + + @Vetoed + static class EnglishGreeting implements Greeting { + + static final String GREETING = "Hello"; + + @Override + public String getGreeting() { + return GREETING + "#" + Thread.currentThread().getName(); + } + + @Override + public String toString() { + return "EnglishGreeting"; + } + } + + @Vetoed + static class CzechGreeting implements Greeting { + + static final String GREETING = "Ahoj"; + + @Override + public String getGreeting() { + return GREETING + "#" + Thread.currentThread().getName(); + } + + @Override + public String toString() { + return "CzechGreeting"; + } + } + + @FunctionalInterface + static interface Greeting2 { + String getGreeting(); + } + + @FunctionalInterface + static interface Greeting { + String getGreeting(); + } +} diff --git a/incubator/cdi-inject-weld/src/test/resources/META-INF/beans.xml b/incubator/cdi-inject-weld/src/test/resources/META-INF/beans.xml new file mode 100644 index 0000000000..678e3dc042 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/resources/META-INF/beans.xml @@ -0,0 +1,20 @@ + + + + diff --git a/incubator/cdi-inject-weld/src/test/resources/META-INF/services/org.glassfish.jersey.inject.weld.spi.BootstrapPreinitialization b/incubator/cdi-inject-weld/src/test/resources/META-INF/services/org.glassfish.jersey.inject.weld.spi.BootstrapPreinitialization new file mode 100644 index 0000000000..7909bcf122 --- /dev/null +++ b/incubator/cdi-inject-weld/src/test/resources/META-INF/services/org.glassfish.jersey.inject.weld.spi.BootstrapPreinitialization @@ -0,0 +1,17 @@ + +# Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License v. 2.0, which is available at +# http://www.eclipse.org/legal/epl-2.0. +# +# This Source Code may also be made available under the following Secondary +# Licenses when the conditions for such availability set forth in the +# Eclipse Public License v. 2.0 are satisfied: GNU General Public License, +# version 2 with the GNU Classpath Exception, which is available at +# https://www.gnu.org/software/classpath/license.html. +# +# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +# + +org.glassfish.jersey.inject.weld.internal.managed.TestPreinitialization \ No newline at end of file diff --git a/incubator/pom.xml b/incubator/pom.xml index fd6b57b8c3..930f5f8e8d 100644 --- a/incubator/pom.xml +++ b/incubator/pom.xml @@ -36,6 +36,7 @@ + cdi-inject-weld declarative-linking gae-integration html-json diff --git a/tests/e2e-inject/cdi-inject-weld/pom.xml b/tests/e2e-inject/cdi-inject-weld/pom.xml new file mode 100644 index 0000000000..60690bf5ef --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/pom.xml @@ -0,0 +1,87 @@ + + + + + 4.0.0 + + + org.glassfish.jersey.tests + e2e-inject + 2.35-SNAPSHOT + + + e2e-inject-cdi-inject-weld + jar + jersey-tests-e2e-inject-cdi-inject-weld + + Jersey E2E Inject CDI SE tests + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + org.jboss.weld.se + weld-se-core + test + + + org.glassfish.jersey.incubator + jersey-cdi-inject-weld + ${project.version} + test + + + org.glassfish.jersey.core + jersey-server + + + org.glassfish.jersey.test-framework.providers + jersey-test-framework-provider-bundle + pom + test + + + org.glassfish.jersey.media + jersey-media-jaxb + + + jakarta.xml.bind + jakarta.xml.bind-api + + + + + org.glassfish.jersey.test-framework + jersey-test-framework-util + test + + + org.hamcrest + hamcrest-library + test + + + junit + junit + test + + + diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Account.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Account.java new file mode 100644 index 0000000000..c09845a497 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Account.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; + +/** + * Keeps current state of money. + * + * @author Petr Bouda + */ +@ApplicationScoped +public class Account { + + private long current = 0; + + public void observeCredit(@Observes @Credit Long amount) { + current += amount; + } + + public void observeDebit(@Observes @Debit Long amount) { + current -= amount; + } + + public long getCurrent() { + return current; + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/AccountResource.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/AccountResource.java new file mode 100644 index 0000000000..49ef2a996a --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/AccountResource.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld; + +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; + +import javax.enterprise.event.Event; +import javax.inject.Inject; + +/** + * Testing resource for CDI events. + * + * @author Petr Bouda + */ +@Path("account") +public class AccountResource { + + @Inject + @Credit + private Event creditEvent; + + @Inject + @Debit + private Event debitEvent; + + @Inject + private Account account; + + @POST + public void credit(@QueryParam("amount") long amount) { + creditEvent.fire(amount); + } + + @DELETE + public void debit(@QueryParam("amount") long amount) { + debitEvent.fire(amount); + } + + @GET + public long current() { + return account.getCurrent(); + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Credit.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Credit.java new file mode 100644 index 0000000000..14ed675394 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Credit.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Event qualifier. + * + * @author Petr Bouda + */ +@Qualifier +@Target({METHOD, FIELD, PARAMETER, TYPE}) +@Retention(RUNTIME) +public @interface Credit { +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Debit.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Debit.java new file mode 100644 index 0000000000..56d60d93e1 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Debit.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Event qualifier. + * + * @author Petr Bouda + */ +@Qualifier +@Target({METHOD, FIELD, PARAMETER, TYPE}) +@Retention(RUNTIME) +public @interface Debit { +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Hello.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Hello.java new file mode 100644 index 0000000000..8d9fc45469 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Hello.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld; + +/** + * Interface used to activate decorator. + * + * @author Petr Bouda + */ +public interface Hello { + + String hello(); + +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/HelloResource.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/HelloResource.java new file mode 100644 index 0000000000..512506f198 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/HelloResource.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import javax.inject.Inject; +import org.glassfish.jersey.tests.e2e.inject.cdi.weld.subresources.MyBean; + +/** + * Intercepted and decorated resource. + * + * @author Petr Bouda + */ +@Secured +@Path("intercepted") +public class HelloResource implements Hello { + + @Inject + private NameService service; + + @Inject + MyBean mybean; + + @GET + @Override + public String hello() { + System.out.println(mybean.hello()); + return "Hello " + service.getName(); + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/HelloStarDecorator.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/HelloStarDecorator.java new file mode 100644 index 0000000000..cbd3ad718c --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/HelloStarDecorator.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld; + +import javax.decorator.Decorator; +import javax.decorator.Delegate; +import javax.enterprise.inject.Any; +import javax.inject.Inject; + +/** + * Decorator wraps the hello resource by stars. + * + * @author Petr Bouda + */ +@Decorator +public class HelloStarDecorator implements Hello { + + @Inject + @Delegate + @Any + Hello account; + + @Override + public String hello() { + return "***" + account.hello() + "***"; + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/JaxrsService.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/JaxrsService.java new file mode 100644 index 0000000000..28471704a4 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/JaxrsService.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld; + +import javax.ws.rs.core.Context; +import javax.ws.rs.core.UriInfo; + +import javax.enterprise.context.ApplicationScoped; + +/** + * Holder for JAX-RS information to inject them into interceptor. JAX-RS does not work in interceptor. + * + * @author Petr Bouda + */ +@ApplicationScoped +public class JaxrsService { + + @Context + private UriInfo uriInfo; + + public UriInfo getUriInfo() { + return uriInfo; + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/NameService.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/NameService.java new file mode 100644 index 0000000000..5205ffbca0 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/NameService.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld; + +import javax.enterprise.context.ApplicationScoped; + +/** + * Service returning the name. + * + * @author Petr Bouda + */ +@ApplicationScoped +public class NameService { + + public static final String NAME = "James"; + + public String getName() { + return NAME; + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Secured.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Secured.java new file mode 100644 index 0000000000..f3971ea4fe --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/Secured.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld; + +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import javax.interceptor.InterceptorBinding; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Security annotation bound to {@link SecurityInterceptor}. + * + * @author Petr Bouda + */ +@Inherited +@InterceptorBinding +@Retention(RUNTIME) +@Target({ METHOD, TYPE }) +public @interface Secured { +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/SecurityInterceptor.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/SecurityInterceptor.java new file mode 100644 index 0000000000..a2e3966709 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/SecurityInterceptor.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld; + +import javax.ws.rs.ForbiddenException; +import javax.ws.rs.core.MultivaluedMap; + +import javax.inject.Inject; +import javax.interceptor.AroundInvoke; +import javax.interceptor.Interceptor; +import javax.interceptor.InvocationContext; + +/** + * Interceptor checking James as a user in query params. + * + * @author Petr Bouda + */ +@Secured +@Interceptor +public class SecurityInterceptor { + + @Inject + NameService nameService; + + @Inject + JaxrsService jaxrsService; + + @AroundInvoke + public Object logMethodEntry(InvocationContext ctx) throws Exception { + MultivaluedMap params = jaxrsService.getUriInfo().getQueryParameters(); + String user = params.getFirst("user"); + + if (nameService.getName().equals(user)) { + return ctx.proceed(); + } else { + throw new ForbiddenException("Forbidden resource for the user: " + user); + } + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/ApplicationCounterBean.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/ApplicationCounterBean.java new file mode 100644 index 0000000000..9835419de9 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/ApplicationCounterBean.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld.scopes; + +import java.util.concurrent.atomic.AtomicInteger; + +import javax.enterprise.context.ApplicationScoped; + +/** + * Request counter. + * + * @author Petr Bouda + */ +@ApplicationScoped +public class ApplicationCounterBean { + + private AtomicInteger counter = new AtomicInteger(); + + public int getNumber() { + return counter.incrementAndGet(); + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/RequestScopedResource.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/RequestScopedResource.java new file mode 100644 index 0000000000..1e24e8d170 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/RequestScopedResource.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld.scopes; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.UriInfo; + +import javax.inject.Inject; + +import org.glassfish.jersey.process.internal.RequestScoped; + +/** + * Request scoped resource. + * + * @author Petr Bouda + */ +@RequestScoped +@Path("request") +public class RequestScopedResource { + + @Inject + private ApplicationCounterBean application; + + @PathParam("name") + private String name; + + private UriInfo uriInfo; + + public RequestScopedResource(@Context UriInfo uriInfo) { + this.uriInfo = uriInfo; + } + + @GET + @Path("{name}") + @Produces("text/plain") + public String getHello() { + return "Hello_" + name + " [" + application.getNumber() + "] " + "[" + uriInfo.getPath() + "] " + this; + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/SingletonScopedResource.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/SingletonScopedResource.java new file mode 100644 index 0000000000..ed32ad19b3 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/SingletonScopedResource.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld.scopes; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.UriInfo; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Singleton Resource. + * + * @author Petr Bouda + */ +@Singleton +@Path("singleton") +public class SingletonScopedResource { + + @Inject + private ApplicationCounterBean application; + + @Context + private UriInfo uriInfo; + + @GET + @Path("{name}") + @Produces("text/plain") + public String getHello(@PathParam("name") String name) { + return "Hello_" + name + " [" + application.getNumber() + "] " + "[" + uriInfo.getPath() + "] " + this; + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/ModelProcessorFeature.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/ModelProcessorFeature.java new file mode 100644 index 0000000000..7eb93572a4 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/ModelProcessorFeature.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld.subresources; + +import javax.enterprise.context.ApplicationScoped; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.core.Configuration; +import javax.ws.rs.core.Feature; +import javax.ws.rs.core.FeatureContext; + +import javax.annotation.Priority; +import javax.inject.Singleton; + +import org.glassfish.jersey.process.Inflector; +import org.glassfish.jersey.process.internal.RequestScoped; +import org.glassfish.jersey.server.model.ModelProcessor; +import org.glassfish.jersey.server.model.Resource; +import org.glassfish.jersey.server.model.ResourceModel; + +@ApplicationScoped +public class ModelProcessorFeature implements Feature { + + @Override + public boolean configure(FeatureContext context) { + context.register(SimpleModelProcessor.class); + return true; + } + + @Priority(5000) + public static class SimpleModelProcessor implements ModelProcessor { + + @Override + public ResourceModel processResourceModel(ResourceModel resourceModel, Configuration configuration) { + ResourceModel.Builder builder = new ResourceModel.Builder(resourceModel.getRootResources(), false); + final Resource singletonResource = Resource.from(SingletonResource.class); + builder.addResource(singletonResource); + + final Resource requestScopeResource = Resource.from(RequestScopeResource.class); + builder.addResource(requestScopeResource); + + final Resource.Builder resourceBuilder = Resource.builder("instance"); + resourceBuilder.addMethod("GET").handledBy(new SimpleModelProcessorInflector()); + final Resource instanceResource = resourceBuilder.build(); + + builder.addResource(instanceResource); + + return builder.build(); + } + + @Override + public ResourceModel processSubResource(ResourceModel subResource, Configuration configuration) { + final Resource resource = Resource.builder() + .mergeWith(Resource.from(EnhancedSubResourceSingleton.class)) + .mergeWith(Resource.from(EnhancedSubResource.class)) + .mergeWith(subResource.getResources().get(0)).build(); + + return new ResourceModel.Builder(true).addResource(resource).build(); + } + + @Priority(5000) + private static class SimpleModelProcessorInflector implements Inflector { + private int counter = 0; + + @Override + public String apply(ContainerRequestContext containerRequestContext) { + return String.valueOf("Inflector:" + counter++); + } + } + } + + @Singleton + public static class EnhancedSubResourceSingleton { + private int counter = 0; + + @GET + @Path("enhanced-singleton") + public String get() { + return "EnhancedSubResourceSingleton:" + String.valueOf(counter++); + } + } + + @RequestScoped + public static class EnhancedSubResource { + + private int counter = 0; + + @GET + @Path("enhanced") + public String get() { + return String.valueOf("EnhancedSubResource:" + counter++); + } + } + + @Path("request-scope") + public static class RequestScopeResource { + private int counter = 0; + + @GET + public String get() { + return String.valueOf("RequestScopeResource:" + counter++); + } + } + + @Path("singleton") + @Singleton + public static class SingletonResource { + private int counter = 0; + + @GET + public String get() { + return String.valueOf("SingletonResource:" + counter++); + } + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/MyBean.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/MyBean.java new file mode 100644 index 0000000000..29127dfdb1 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/MyBean.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld.subresources; + +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; +import org.glassfish.jersey.tests.e2e.inject.cdi.weld.Hello; +import org.glassfish.jersey.tests.e2e.inject.cdi.weld.NameService; +import org.glassfish.jersey.tests.e2e.inject.cdi.weld.Secured; + +@ApplicationScoped +@Secured +public class MyBean implements Hello { + @Inject + private NameService service; + + public String hello() { + System.out.println("mybean"); + return "Hello " + service.getName(); + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/RootResource.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/RootResource.java new file mode 100644 index 0000000000..00d4922154 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/RootResource.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld.subresources; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +@Path("root") +public class RootResource { + + @GET + public String get() { + return "root"; + } + + @Path("sub-resource-singleton") + public Class getSubResourceSingleton() { + return SubResourceSingleton.class; + } + + @Path("sub-resource-instance") + public SubResourceSingleton getSubResourceSingletonInstance() { + return new SubResourceSingleton(); + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/RootSingletonResource.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/RootSingletonResource.java new file mode 100644 index 0000000000..14b273421a --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/RootSingletonResource.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld.subresources; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +import javax.inject.Singleton; + +@Path("root-singleton") +@Singleton +public class RootSingletonResource { + + private int counter = 0; + + @GET + public String get() { + return "RootSingletonResource:" + String.valueOf(counter++); + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/SubResourceSingleton.java b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/SubResourceSingleton.java new file mode 100644 index 0000000000..aacd4422ee --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/SubResourceSingleton.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld.subresources; + +import javax.ws.rs.GET; + +import javax.inject.Singleton; + +@Singleton +public class SubResourceSingleton { + + private int counter = 0; + + @GET + public String get() { + return String.valueOf("SubResourceSingleton:" + counter++); + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/main/resources/META-INF/beans.xml b/tests/e2e-inject/cdi-inject-weld/src/main/resources/META-INF/beans.xml new file mode 100644 index 0000000000..ee4acc616d --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/main/resources/META-INF/beans.xml @@ -0,0 +1,31 @@ + + + + + + org.glassfish.jersey.tests.e2e.inject.cdi.weld.SecurityInterceptor + + + org.glassfish.jersey.tests.e2e.inject.cdi.weld.HelloStarDecorator + + diff --git a/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/EventsTest.java b/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/EventsTest.java new file mode 100644 index 0000000000..4c0cb297b8 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/EventsTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Response; + +import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; + +import org.jboss.weld.environment.se.Weld; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +/** + * Tests that the resource can fire an event. + * + * @author Petr Bouda + */ +public class EventsTest extends JerseyTest { + + private Weld weld; + + @Before + public void setup() { + Assume.assumeTrue(Hk2InjectionManagerFactory.isImmediateStrategy()); + } + + @Override + public void setUp() throws Exception { + if (Hk2InjectionManagerFactory.isImmediateStrategy()) { + weld = new Weld(); + weld.initialize(); + super.setUp(); + } + } + + @Override + public void tearDown() throws Exception { + if (Hk2InjectionManagerFactory.isImmediateStrategy()) { + weld.shutdown(); + super.tearDown(); + } + } + + @Override + protected Application configure() { + return new ResourceConfig(AccountResource.class); + } + + @Test + public void testFiredEvents() { + Response credit = target("account").queryParam("amount", 50).request().post(Entity.json("")); + assertEquals(204, credit.getStatus()); + + Response debit = target("account").queryParam("amount", 25).request().delete(); + assertEquals(204, debit.getStatus()); + + Long current = target("account").queryParam("amount", 25).request().get(Long.class); + assertEquals(25, current.longValue()); + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/InterceptorDecoratorTest.java b/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/InterceptorDecoratorTest.java new file mode 100644 index 0000000000..a7752bdcab --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/InterceptorDecoratorTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld; + +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Response; + +import org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; + +import org.jboss.weld.environment.se.Weld; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +/** + * Tests that the resource can be intercepted and decorated. + * + * @author Petr Bouda + */ +public class InterceptorDecoratorTest extends JerseyTest { + + private Weld weld; + + @Before + public void setup() { + Assume.assumeTrue(Hk2InjectionManagerFactory.isImmediateStrategy()); + } + + @Override + public void setUp() throws Exception { + if (Hk2InjectionManagerFactory.isImmediateStrategy()) { + weld = new Weld(); + weld.initialize(); + super.setUp(); + } + } + + @Override + public void tearDown() throws Exception { + if (Hk2InjectionManagerFactory.isImmediateStrategy()) { + weld.shutdown(); + super.tearDown(); + } + } + + @Override + protected Application configure() { + return new ResourceConfig(HelloResource.class); + } + + @Test + public void testInterceptedGet() { + String intercepted = target("intercepted").queryParam("user", NameService.NAME).request().get(String.class); + assertEquals("***Hello James***", intercepted); + } + + @Test + public void testForbiddenGet() { + Response result = target("intercepted").queryParam("user", "unknown").request().get(); + assertEquals(403, result.getStatus()); + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/RequestContextBuilder.java b/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/RequestContextBuilder.java new file mode 100644 index 0000000000..d4cc5c5eab --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/RequestContextBuilder.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.net.URI; +import java.util.Arrays; +import java.util.Collections; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Cookie; +import javax.ws.rs.core.GenericEntity; +import javax.ws.rs.core.GenericType; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.core.SecurityContext; +import javax.ws.rs.ext.WriterInterceptor; + +import org.glassfish.jersey.internal.MapPropertiesDelegate; +import org.glassfish.jersey.internal.PropertiesDelegate; +import org.glassfish.jersey.message.MessageBodyWorkers; +import org.glassfish.jersey.message.internal.HeaderUtils; +import org.glassfish.jersey.server.ContainerRequest; + +/** + * Used by tests to create mock JerseyContainerRequestContext instances. + * + * @author Martin Matula + */ +public class RequestContextBuilder { + + public class TestContainerRequest extends ContainerRequest { + + private Object entity; + private GenericType entityType; + private final PropertiesDelegate propertiesDelegate; + + public TestContainerRequest(final URI baseUri, + final URI requestUri, + final String method, + final SecurityContext securityContext, + final PropertiesDelegate propertiesDelegate) { + super(baseUri, requestUri, method, securityContext, propertiesDelegate, null); + this.propertiesDelegate = propertiesDelegate; + } + + public void setEntity(final Object entity) { + if (entity instanceof GenericEntity) { + this.entity = ((GenericEntity) entity).getEntity(); + this.entityType = new GenericType(((GenericEntity) entity).getType()); + } else { + this.entity = entity; + this.entityType = new GenericType(entity.getClass()); + } + } + + @Override + public void setWorkers(final MessageBodyWorkers workers) { + super.setWorkers(workers); + final byte[] entityBytes; + if (entity != null) { + final MultivaluedMap myMap = new MultivaluedHashMap(getHeaders()); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + OutputStream stream = null; + try { + stream = workers.writeTo(entity, entity.getClass(), entityType.getType(), + new Annotation[0], getMediaType(), + myMap, + propertiesDelegate, baos, Collections.emptyList()); + } catch (final IOException | WebApplicationException ex) { + Logger.getLogger(TestContainerRequest.class.getName()).log(Level.SEVERE, null, ex); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (final IOException e) { + // ignore + } + } + } + entityBytes = baos.toByteArray(); + } else { + entityBytes = new byte[0]; + } + setEntityStream(new ByteArrayInputStream(entityBytes)); + } + } + + private final TestContainerRequest request; + + public static RequestContextBuilder from(final String requestUri, final String method) { + return from(null, requestUri, method); + } + + public static RequestContextBuilder from(final String baseUri, final String requestUri, final String method) { + return new RequestContextBuilder(baseUri, requestUri, method); + } + + public static RequestContextBuilder from(final URI requestUri, final String method) { + return from(null, requestUri, method); + } + + public static RequestContextBuilder from(final URI baseUri, final URI requestUri, final String method) { + return new RequestContextBuilder(baseUri, requestUri, method); + } + + private RequestContextBuilder(final String baseUri, final String requestUri, final String method) { + this(baseUri == null || baseUri.isEmpty() ? null : URI.create(baseUri), URI.create(requestUri), method); + } + + private RequestContextBuilder(final URI baseUri, final URI requestUri, final String method) { + request = new TestContainerRequest(baseUri, requestUri, method, null, + new MapPropertiesDelegate()); + } + + public ContainerRequest build() { + return request; + } + + public RequestContextBuilder accept(final String... acceptHeader) { + putHeaders(HttpHeaders.ACCEPT, acceptHeader); + return this; + } + + public RequestContextBuilder accept(final MediaType... acceptHeader) { + putHeaders(HttpHeaders.ACCEPT, (Object[]) acceptHeader); + return this; + } + + public RequestContextBuilder entity(final Object entity) { + request.setEntity(entity); + return this; + } + + public RequestContextBuilder type(final String contentType) { + request.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, contentType); + return this; + + } + + public RequestContextBuilder type(final MediaType contentType) { + request.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, HeaderUtils.asString(contentType, request.getConfiguration())); + return this; + } + + public RequestContextBuilder header(final String name, final Object value) { + putHeader(name, value); + return this; + } + + public RequestContextBuilder cookie(final Cookie cookie) { + putHeader(HttpHeaders.COOKIE, cookie); + return this; + } + + public RequestContextBuilder cookies(final Cookie... cookies) { + putHeaders(HttpHeaders.COOKIE, (Object[]) cookies); + return this; + } + + private void putHeader(final String name, final Object value) { + if (value == null) { + request.getHeaders().remove(name); + return; + } + request.header(name, HeaderUtils.asString(value, request.getConfiguration())); + } + + private void putHeaders(final String name, final Object... values) { + if (values == null) { + request.getHeaders().remove(name); + return; + } + request.getHeaders().addAll(name, HeaderUtils.asStringList(Arrays.asList(values), request.getConfiguration())); + } + + private void putHeaders(final String name, final String... values) { + if (values == null) { + request.getHeaders().remove(name); + return; + } + request.getHeaders().addAll(name, values); + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/ScopesTest.java b/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/ScopesTest.java new file mode 100644 index 0000000000..9f7522b111 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/scopes/ScopesTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld.scopes; + +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; + +import org.jboss.weld.environment.se.Weld; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +/** + * Tests CDI resources. + */ +public class ScopesTest extends JerseyTest { + + private Weld weld; + + @Override + public void setUp() throws Exception { + weld = new Weld(); + weld.initialize(); + super.setUp(); + } + + @Override + public void tearDown() throws Exception { + weld.shutdown(); + super.tearDown(); + } + + @Override + protected ResourceConfig configure() { + return new ResourceConfig(RequestScopedResource.class, SingletonScopedResource.class); + } + + @Test + public void testCheckRequest() throws InterruptedException { + String[] response1 = target().path("request").path("James").request().get(String.class).split(" "); + String[] response2 = target().path("request").path("Marcus").request().get(String.class).split(" "); + assertResponses("request", response1, response2); + assertNotEquals(response1[3], response2[3]); + } + + @Test + public void testCheckSingleton() throws InterruptedException { + String[] response1 = target().path("singleton").path("James").request().get(String.class).split(" "); + String[] response2 = target().path("singleton").path("Marcus").request().get(String.class).split(" "); + assertResponses("singleton", response1, response2); + assertEquals(response1[3], response2[3]); + } + + private void assertResponses(String type, String[] response1, String[] response2) { + assertEquals("Hello_James", response1[0]); + assertEquals("[1]", response1[1]); + assertEquals("[" + type + "/James]", response1[2]); + + assertEquals("Hello_Marcus", response2[0]); + assertEquals("[2]", response2[1]); + assertEquals("[" + type + "/Marcus]", response2[2]); + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/ModelProcessorScopeTest.java b/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/ModelProcessorScopeTest.java new file mode 100644 index 0000000000..a26cb29654 --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/test/java/org/glassfish/jersey/tests/e2e/inject/cdi/weld/subresources/ModelProcessorScopeTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.tests.e2e.inject.cdi.weld.subresources; + +import java.util.concurrent.ExecutionException; + +import org.glassfish.jersey.server.ApplicationHandler; +import org.glassfish.jersey.server.ContainerResponse; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.tests.e2e.inject.cdi.weld.RequestContextBuilder; + +import org.jboss.weld.environment.se.Weld; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +/** + * Test scope of resources enhanced by model processors. + * + * @author Miroslav Fuksa + * + */ +public class ModelProcessorScopeTest { + + private Weld weld; + + @Before + public void setUp() throws Exception { + weld = new Weld(); + weld.initialize(); + } + + @After + public void tearDown() throws Exception { + weld.shutdown(); + } + + private void _testCounter(ApplicationHandler applicationHandler, String requestUri, final String prefix, + final String expectedSecondHit) throws + InterruptedException, ExecutionException { + ContainerResponse response = applicationHandler.apply(RequestContextBuilder.from(requestUri, + "GET").build()).get(); + assertEquals(200, response.getStatus()); + assertEquals(prefix + ":0", response.getEntity()); + response = applicationHandler.apply(RequestContextBuilder.from(requestUri, + "GET").build()).get(); + assertEquals(prefix + ":" + expectedSecondHit, response.getEntity()); + } + + @Test + public void testSingleton() throws ExecutionException, InterruptedException { + ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(ModelProcessorFeature + .SingletonResource.class)); + final String requestUri = "/singleton"; + _testCounter(applicationHandler, requestUri, "SingletonResource", "1"); + } + + @Test + public void testSingletonInModelProcessor() throws ExecutionException, InterruptedException { + ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class, + ModelProcessorFeature.class)); + final String requestUri = "/singleton"; + _testCounter(applicationHandler, requestUri, "SingletonResource", "1"); + } + + @Test + public void testSubResourceSingletonInOriginalModel() throws ExecutionException, InterruptedException { + ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class)); + final String requestUri = "/root/sub-resource-singleton"; + _testCounter(applicationHandler, requestUri, "SubResourceSingleton", "1"); + } + + @Test + public void testSubResourceEnhancedSingleton() throws ExecutionException, InterruptedException { + ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class)); + final String requestUri = "/root/sub-resource-singleton/enhanced-singleton"; + _testCounter(applicationHandler, requestUri, "EnhancedSubResourceSingleton", "1"); + } + + @Test + public void testSubResourceInstanceEnhancedSingleton() throws ExecutionException, InterruptedException { + ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class)); + final String requestUri = "/root/sub-resource-instance/enhanced-singleton"; + _testCounter(applicationHandler, requestUri, "EnhancedSubResourceSingleton", "1"); + } + + @Test + public void testSubResourceInstanceEnhancedSubResource() throws ExecutionException, InterruptedException { + ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class)); + final String requestUri = "/root/sub-resource-instance/enhanced"; + _testCounter(applicationHandler, requestUri, "EnhancedSubResource", "0"); + } + + @Test + public void testSubResourceEnhancedSubResource() throws ExecutionException, InterruptedException { + ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class)); + final String requestUri = "/root/sub-resource-singleton/enhanced"; + _testCounter(applicationHandler, requestUri, "EnhancedSubResource", "0"); + } + + @Test + public void testInstanceInModelProcessor() throws ExecutionException, InterruptedException { + ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class, + ModelProcessorFeature.class)); + final String requestUri = "/instance"; + _testCounter(applicationHandler, requestUri, "Inflector", "1"); + } + + @Test + public void testRootSingleton() throws ExecutionException, InterruptedException { + ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class, + RootSingletonResource.class)); + final String requestUri = "/root-singleton"; + _testCounter(applicationHandler, requestUri, "RootSingletonResource", "1"); + } + + @Test + public void testRequestScopeResource() throws ExecutionException, InterruptedException { + ApplicationHandler applicationHandler = new ApplicationHandler(new ResourceConfig(RootResource.class, + RootSingletonResource.class, ModelProcessorFeature.class)); + final String requestUri = "/request-scope"; + _testCounter(applicationHandler, requestUri, "RequestScopeResource", "0"); + } +} diff --git a/tests/e2e-inject/cdi-inject-weld/src/test/resources/surefire.policy b/tests/e2e-inject/cdi-inject-weld/src/test/resources/surefire.policy new file mode 100644 index 0000000000..a6cc723fab --- /dev/null +++ b/tests/e2e-inject/cdi-inject-weld/src/test/resources/surefire.policy @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +// we do not care about java lib itself +grant codebase "file:${java.home}/-" { + permission java.security.AllPermission; +}; + +// we do not care about our dependencies +grant codebase "file:${settings.localRepository}/-" { + permission java.security.AllPermission; +}; + +// this is to be able to set runtime delegate instance in jax-rs from the tests +// and to run multi-threaded tests +grant codebase "file:${project.build.directory}/test-classes/-" { + permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; + permission java.lang.RuntimePermission "modifyThread"; + permission java.util.PropertyPermission "*", "write"; + permission java.lang.RuntimePermission "getClassLoader"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.misc"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.misc.*"; + permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; +}; + +grant codebase "file:${project.build.directory}/classes/-" { + permission java.lang.RuntimePermission "accessClassInPackage.sun.misc"; + permission java.lang.RuntimePermission "accessDeclaredMembers"; + permission java.lang.RuntimePermission "getClassLoader"; + permission java.lang.RuntimePermission "modifyThread"; + permission java.util.PropertyPermission "*", "read"; + permission java.io.FilePermission "<>", "read"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.misc"; + permission java.lang.RuntimePermission "accessClassInPackage.sun.misc.*"; + permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; +}; diff --git a/tests/e2e-inject/pom.xml b/tests/e2e-inject/pom.xml index 142e17b33b..adab97d9d2 100644 --- a/tests/e2e-inject/pom.xml +++ b/tests/e2e-inject/pom.xml @@ -34,6 +34,7 @@ cdi2-se + cdi-inject-weld hk2 diff --git a/tests/integration/cdi-integration/context-inject-on-server/pom.xml b/tests/integration/cdi-integration/context-inject-on-server/pom.xml index 38b31ca99d..f405087ad3 100644 --- a/tests/integration/cdi-integration/context-inject-on-server/pom.xml +++ b/tests/integration/cdi-integration/context-inject-on-server/pom.xml @@ -56,25 +56,12 @@ jersey-test-framework-provider-grizzly2 test - - org.glassfish.jersey.ext.cdi - jersey-weld2-se - test - org.jboss.weld.se weld-se-core test - - org.glassfish.jersey.ext.cdi - jersey-cdi1x - - - org.glassfish.jersey.ext.cdi - jersey-cdi-rs-inject - - + org.glassfish.jersey.media jersey-media-sse @@ -83,4 +70,54 @@ jersey-container-servlet-core + + + DefaultInject + + !cdiInject + + + + org.glassfish.jersey.ext.cdi + jersey-weld2-se + test + + + org.glassfish.jersey.ext.cdi + jersey-cdi1x + + + org.glassfish.jersey.ext.cdi + jersey-cdi-rs-inject + + + + + CdiInjectWeld + + cdiInject + + + + org.glassfish.jersey.incubator + jersey-cdi-inject-weld + ${project.version} + test + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org/glassfish/jersey/tests/cdi/inject/ScopedInjectionTest.java + + + + + + + diff --git a/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ApplicationInjectParent.java b/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ApplicationInjectParent.java index fc6894a6fc..5b4de38711 100644 --- a/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ApplicationInjectParent.java +++ b/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ApplicationInjectParent.java @@ -51,11 +51,11 @@ public class ApplicationInjectParent extends Application { @Inject protected HttpHeaders injectHttpHeaders; - @Context - ParamConverterProvider contextParamConverterProvider; - - @Inject - ParamConverterProvider injectParamConverterProvider; +// @Context +// ParamConverterProvider contextParamConverterProvider; +// +// @Inject +// ParamConverterProvider injectParamConverterProvider; @Context protected Providers contextProviders; diff --git a/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ParentContainerRequestFilter.java b/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ParentContainerRequestFilter.java index a326454a40..75a8ccb1e6 100644 --- a/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ParentContainerRequestFilter.java +++ b/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ParentContainerRequestFilter.java @@ -28,11 +28,11 @@ default void filter(ContainerRequestContext requestContext) throws IOException { StringBuilder stringBuilder = new StringBuilder(); boolean injected = false; - if (requestContext.getUriInfo().getRequestUri().toASCIIString().contains("injected")) { + if (requestContext.getUriInfo().getRequestUri().toASCIIString().contains("inject")) { injected = checkInjected(stringBuilder); } - if (requestContext.getUriInfo().getRequestUri().toASCIIString().contains("contexted")) { + if (requestContext.getUriInfo().getRequestUri().toASCIIString().contains("context")) { injected = checkContexted(stringBuilder); } diff --git a/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ParentContainerResponseFilter.java b/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ParentContainerResponseFilter.java index 585f04548b..47a9afc8d6 100644 --- a/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ParentContainerResponseFilter.java +++ b/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ParentContainerResponseFilter.java @@ -28,11 +28,11 @@ default void filter(ContainerRequestContext requestContext, ContainerResponseCon StringBuilder stringBuilder = new StringBuilder(); boolean injected = false; - if (requestContext.getUriInfo().getRequestUri().toASCIIString().contains("injected")) { + if (requestContext.getUriInfo().getRequestUri().toASCIIString().contains("inject")) { injected = checkInjected(stringBuilder); } - if (requestContext.getUriInfo().getRequestUri().toASCIIString().contains("contexted")) { + if (requestContext.getUriInfo().getRequestUri().toASCIIString().contains("context")) { injected = checkContexted(stringBuilder); } diff --git a/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ParentInject.java b/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ParentInject.java index 29e11d81f8..6a546fbe6b 100644 --- a/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ParentInject.java +++ b/tests/integration/cdi-integration/context-inject-on-server/src/main/java/org/glassfish/jersey/tests/cdi/inject/ParentInject.java @@ -55,11 +55,11 @@ public class ParentInject implements ParentChecker { @Inject protected HttpHeaders injectHttpHeaders; - @Context - protected ParamConverterProvider contextParamConverterProvider; - - @Inject - protected ParamConverterProvider injectParamConverterProvider; +// @Context +// protected ParamConverterProvider contextParamConverterProvider; +// +// @Inject +// protected ParamConverterProvider injectParamConverterProvider; @Context protected PropertiesDelegate contextPropertiesDelegate; @@ -140,7 +140,7 @@ public boolean checkInjected(StringBuilder stringBuilder) { injected &= checkConfiguration(injectConfiguration, stringBuilder); injected &= InjectionChecker.checkHttpHeaders(injectHttpHeaders, stringBuilder); injected &= checkPropertiesDelegate(injectPropertiesDelegate, stringBuilder); - injected &= InjectionChecker.checkParamConverterProvider(injectParamConverterProvider, stringBuilder); +// injected &= InjectionChecker.checkParamConverterProvider(injectParamConverterProvider, stringBuilder); injected &= InjectionChecker.checkProviders(injectProviders, stringBuilder); injected &= InjectionChecker.checkRequest(injectRequest, stringBuilder); injected &= InjectionChecker.checkResourceContext(injectResourceContext, stringBuilder); @@ -163,7 +163,7 @@ public boolean checkContexted(StringBuilder stringBuilder) { injected &= checkApplication(contextApplication, stringBuilder); injected &= checkConfiguration(contextConfiguration, stringBuilder); injected &= InjectionChecker.checkHttpHeaders(contextHttpHeaders, stringBuilder); - injected &= InjectionChecker.checkParamConverterProvider(contextParamConverterProvider, stringBuilder); +// injected &= InjectionChecker.checkParamConverterProvider(contextParamConverterProvider, stringBuilder); injected &= checkPropertiesDelegate(contextPropertiesDelegate, stringBuilder); injected &= InjectionChecker.checkProviders(contextProviders, stringBuilder); injected &= InjectionChecker.checkRequest(contextRequest, stringBuilder);