From dfd0ee7755d6e2408f53c81b19286ce80fa89658 Mon Sep 17 00:00:00 2001 From: Maxim Nesen Date: Fri, 3 Sep 2021 09:35:12 +0200 Subject: [PATCH 01/90] System properties proper config for TimeWindowStatisticsImplTest Signed-off-by: Maxim Nesen --- {etc/jenkins => core-server/etc}/systemPropertiesFile | 0 core-server/pom.xml | 1 + .../internal/monitoring/TimeWindowStatisticsImplTest.java | 6 ------ etc/jenkins/jenkins_build.sh | 2 +- 4 files changed, 2 insertions(+), 7 deletions(-) rename {etc/jenkins => core-server/etc}/systemPropertiesFile (100%) diff --git a/etc/jenkins/systemPropertiesFile b/core-server/etc/systemPropertiesFile similarity index 100% rename from etc/jenkins/systemPropertiesFile rename to core-server/etc/systemPropertiesFile diff --git a/core-server/pom.xml b/core-server/pom.xml index 54c84ffba0..633e841944 100644 --- a/core-server/pom.xml +++ b/core-server/pom.xml @@ -156,6 +156,7 @@ 1 1C true + ${project.basedir}/etc/systemPropertiesFile diff --git a/core-server/src/test/java/org/glassfish/jersey/server/internal/monitoring/TimeWindowStatisticsImplTest.java b/core-server/src/test/java/org/glassfish/jersey/server/internal/monitoring/TimeWindowStatisticsImplTest.java index d92393e347..d8f9a8cf6c 100644 --- a/core-server/src/test/java/org/glassfish/jersey/server/internal/monitoring/TimeWindowStatisticsImplTest.java +++ b/core-server/src/test/java/org/glassfish/jersey/server/internal/monitoring/TimeWindowStatisticsImplTest.java @@ -37,12 +37,6 @@ public class TimeWindowStatisticsImplTest { private static final int COLLISION_BUFFER_POWER = 3; private static final double DELTA = 0.0001; - @BeforeClass - public static void beforeClass() { - System.setProperty(ServerProperties.COLLISION_BUFFER_POWER_JVM_ARG, - Integer.toString(COLLISION_BUFFER_POWER)); - } - @Test public void jvmLoaded() { assertEquals(COLLISION_BUFFER_POWER, ReservoirConstants.COLLISION_BUFFER_POWER); diff --git a/etc/jenkins/jenkins_build.sh b/etc/jenkins/jenkins_build.sh index afa8b1584e..e612cbbc3f 100644 --- a/etc/jenkins/jenkins_build.sh +++ b/etc/jenkins/jenkins_build.sh @@ -2,4 +2,4 @@ export DEBUG=true -mvn -V -U -B -e clean install glassfish-copyright:check -Dcopyright.quiet=false -Dsurefire.systemPropertiesFile=${WORKSPACE}/etc/jenkins/systemPropertiesFile \ No newline at end of file +mvn -V -U -B -e clean install glassfish-copyright:check -Dcopyright.quiet=false \ No newline at end of file From a3b24abf71ff284e2cf1124647e2181f4042c37f Mon Sep 17 00:00:00 2001 From: Andrii Serkes <74911628+aserkes@users.noreply.github.com> Date: Fri, 3 Sep 2021 13:22:15 +0300 Subject: [PATCH 02/90] add possibility to use entity with http method Options in requests according to the RFC 7231 (#4837) * add possibility to use entity with http method Options in requests according to the RFC 7231 Signed-off-by: aserkes --- .../apache/connector/HttpMethodTest.java | 11 ++- .../grizzly/connector/HttpMethodTest.java | 85 +++++++++++++++++++ .../connector/BasicHelidonConnectorTest.java | 11 ++- .../jersey/jdk/connector/HttpMethodTest.java | 85 +++++++++++++++++++ .../jersey/jetty/connector/MethodTest.java | 9 +- .../jersey/netty/connector/MethodTest.java | 9 +- .../jersey/client/JerseyInvocation.java | 2 +- .../e2e/client/HttpMethodEntityTest.java | 4 +- 8 files changed, 209 insertions(+), 7 deletions(-) create mode 100644 connectors/grizzly-connector/src/test/java/org/glassfish/jersey/grizzly/connector/HttpMethodTest.java create mode 100644 connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/HttpMethodTest.java diff --git a/connectors/apache-connector/src/test/java/org/glassfish/jersey/apache/connector/HttpMethodTest.java b/connectors/apache-connector/src/test/java/org/glassfish/jersey/apache/connector/HttpMethodTest.java index 3edabeb6e4..f507504348 100644 --- a/connectors/apache-connector/src/test/java/org/glassfish/jersey/apache/connector/HttpMethodTest.java +++ b/connectors/apache-connector/src/test/java/org/glassfish/jersey/apache/connector/HttpMethodTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -42,6 +42,7 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.junit.Test; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -146,6 +147,14 @@ public void testOptions() { cr.close(); } + @Test + public void testOptionsWithEntity() { + WebTarget r = getWebTarget(); + Response response = r.request().build("OPTIONS", Entity.text("OPTIONS")).invoke(); + assertEquals(200, response.getStatus()); + response.close(); + } + @Test public void testGet() { WebTarget r = getWebTarget(); diff --git a/connectors/grizzly-connector/src/test/java/org/glassfish/jersey/grizzly/connector/HttpMethodTest.java b/connectors/grizzly-connector/src/test/java/org/glassfish/jersey/grizzly/connector/HttpMethodTest.java new file mode 100644 index 0000000000..3f905042e2 --- /dev/null +++ b/connectors/grizzly-connector/src/test/java/org/glassfish/jersey/grizzly/connector/HttpMethodTest.java @@ -0,0 +1,85 @@ +/* + * 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.grizzly.connector; + +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.Test; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Response; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Tests the Http methods. + */ +public class HttpMethodTest extends JerseyTest { + + @Override + protected Application configure() { + return new ResourceConfig(HttpMethodResource.class); + } + + protected Client createClient() { + ClientConfig cc = new ClientConfig(); + cc.connectorProvider(new GrizzlyConnectorProvider()); + return ClientBuilder.newClient(cc); + } + + private WebTarget getWebTarget(final Client client) { + return client.target(getBaseUri()).path("test"); + } + + private WebTarget getWebTarget() { + return getWebTarget(createClient()); + } + + @Path("/test") + public static class HttpMethodResource { + @GET + public String get() { + return "GET"; + } + } + + @Test + public void testOptionsWithEntity() { + WebTarget r = getWebTarget(); + Response response = r.request().build("OPTIONS", Entity.text("OPTIONS")).invoke(); + assertEquals(200, response.getStatus()); + response.close(); + } + + @Test + public void testGet() { + WebTarget r = getWebTarget(); + assertEquals("GET", r.request().get(String.class)); + + Response cr = r.request().get(); + assertTrue(cr.hasEntity()); + cr.close(); + } +} diff --git a/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/BasicHelidonConnectorTest.java b/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/BasicHelidonConnectorTest.java index 8b42df173f..4af0ebec99 100644 --- a/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/BasicHelidonConnectorTest.java +++ b/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/BasicHelidonConnectorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 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 @@ -55,6 +55,8 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import static org.junit.Assert.assertEquals; + @RunWith(Parameterized.class) public class BasicHelidonConnectorTest extends JerseyTest { @@ -305,4 +307,11 @@ public void testOneClientsTwoReqestsAsync() throws ExecutionException, Interrupt Assert.assertEquals("long", longResponse.readEntity(String.class)); } } + + @Test + public void testOptionsWithEntity() { + Response response = target("basic").path("get").request().build("OPTIONS", Entity.text("OPTIONS")).invoke(); + assertEquals(200, response.getStatus()); + response.close(); + } } diff --git a/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/HttpMethodTest.java b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/HttpMethodTest.java new file mode 100644 index 0000000000..60f5f1bc6d --- /dev/null +++ b/connectors/jdk-connector/src/test/java/org/glassfish/jersey/jdk/connector/HttpMethodTest.java @@ -0,0 +1,85 @@ +/* + * 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.jdk.connector; + +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.Test; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.Application; +import javax.ws.rs.core.Response; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Tests the Http methods. + */ +public class HttpMethodTest extends JerseyTest { + + @Override + protected Application configure() { + return new ResourceConfig(HttpMethodResource.class); + } + + protected Client createClient() { + ClientConfig cc = new ClientConfig(); + cc.connectorProvider(new JdkConnectorProvider()); + return ClientBuilder.newClient(cc); + } + + private WebTarget getWebTarget(final Client client) { + return client.target(getBaseUri()).path("test"); + } + + private WebTarget getWebTarget() { + return getWebTarget(createClient()); + } + + @Path("/test") + public static class HttpMethodResource { + @GET + public String get() { + return "GET"; + } + } + + @Test + public void testOptionsWithEntity() { + WebTarget r = getWebTarget(); + Response response = r.request().build("OPTIONS", Entity.text("OPTIONS")).invoke(); + assertEquals(200, response.getStatus()); + response.close(); + } + + @Test + public void testGet() { + WebTarget r = getWebTarget(); + assertEquals("GET", r.request().get(String.class)); + + Response cr = r.request().get(); + assertTrue(cr.hasEntity()); + cr.close(); + } +} diff --git a/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/MethodTest.java b/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/MethodTest.java index 78393eb7bc..6ce7a9198f 100644 --- a/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/MethodTest.java +++ b/connectors/jetty-connector/src/test/java/org/glassfish/jersey/jetty/connector/MethodTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -143,4 +143,11 @@ public void testPatch() { Response response = target(PATH).request().method("PATCH", Entity.entity("PATCH", MediaType.TEXT_PLAIN)); assertEquals("PATCH", response.readEntity(String.class)); } + + @Test + public void testOptionsWithEntity() { + Response response = target(PATH).request().build("OPTIONS", Entity.text("OPTIONS")).invoke(); + assertEquals(200, response.getStatus()); + response.close(); + } } diff --git a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/MethodTest.java b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/MethodTest.java index c37cf35ca7..f0c639b283 100644 --- a/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/MethodTest.java +++ b/connectors/netty-connector/src/test/java/org/glassfish/jersey/netty/connector/MethodTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -98,4 +98,11 @@ public void testDelete() { Response response = target(PATH).request().delete(); assertEquals("DELETE", response.readEntity(String.class)); } + + @Test + public void testOptionsWithEntity() { + Response response = target(PATH).request().build("OPTIONS", Entity.text("OPTIONS")).invoke(); + assertEquals(200, response.getStatus()); + response.close(); + } } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java b/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java index f6a48d7f1e..2f59ae7f00 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/JerseyInvocation.java @@ -123,7 +123,7 @@ private static Map initializeMap() { map.put("DELETE", EntityPresence.MUST_BE_NULL); map.put("GET", EntityPresence.MUST_BE_NULL); map.put("HEAD", EntityPresence.MUST_BE_NULL); - map.put("OPTIONS", EntityPresence.MUST_BE_NULL); + map.put("OPTIONS", EntityPresence.OPTIONAL); map.put("PATCH", EntityPresence.MUST_BE_PRESENT); map.put("POST", EntityPresence.OPTIONAL); // we allow to post null instead of entity map.put("PUT", EntityPresence.MUST_BE_PRESENT); diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpMethodEntityTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpMethodEntityTest.java index 175ad5fe90..3a81437d0c 100644 --- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpMethodEntityTest.java +++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/HttpMethodEntityTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -85,7 +85,7 @@ public void testHead() { @Test public void testOptions() { - _test("OPTIONS", true, true); + _test("OPTIONS", true, false); _test("OPTIONS", false, false); } 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 03/90] 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: + *