Allow HK2 AbstractBinder class to bind before the Feature is called (#4394)
Signed-off-by: Jan Supol <jan.supol@oracle.com>
diff --git a/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java b/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java
index ca51259..d70faea 100644
--- a/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java
+++ b/core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2020 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
@@ -28,6 +28,7 @@
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
+import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.logging.Level;
@@ -618,15 +619,19 @@
* @param injectionManager injection manager in which the binders and features should be configured.
*/
public void configureMetaProviders(InjectionManager injectionManager, ManagedObjectsFinalizer finalizer) {
+ final Set<Object> configuredExternals = Collections.newSetFromMap(new IdentityHashMap<>());
+
// First, configure existing binders
- Set<Binder> configuredBinders = configureBinders(injectionManager, Collections.emptySet());
+ final Set<Binder> configuredBinders = configureBinders(injectionManager, Collections.emptySet());
// Check whether meta providers have been initialized for a config this config has been loaded from.
if (!disableMetaProviderConfiguration) {
+ // Next, register external meta objects
+ configureExternalObjects(injectionManager, configuredExternals);
// Configure all features
configureFeatures(injectionManager, new HashSet<>(), resetRegistrations(), finalizer);
- // Next, register external meta objects
- configureExternalObjects(injectionManager);
+ // Next, register external meta objects registered by features
+ configureExternalObjects(injectionManager, configuredExternals);
// At last, configure any new binders added by features
configureBinders(injectionManager, configuredBinders);
}
@@ -653,11 +658,17 @@
.collect(Collectors.toList());
}
- private void configureExternalObjects(InjectionManager injectionManager) {
+ private void configureExternalObjects(InjectionManager injectionManager, Set<Object> externalObjects) {
+ Consumer<Object> registerOnce = o -> {
+ if (!externalObjects.contains(o)) {
+ injectionManager.register(o);
+ externalObjects.add(o);
+ }
+ };
componentBag.getInstances(model -> ComponentBag.EXTERNAL_ONLY.test(model, injectionManager))
- .forEach(injectionManager::register);
+ .forEach(registerOnce);
componentBag.getClasses(model -> ComponentBag.EXTERNAL_ONLY.test(model, injectionManager))
- .forEach(injectionManager::register);
+ .forEach(registerOnce);
}
private void configureFeatures(InjectionManager injectionManager,
diff --git a/tests/e2e-inject/hk2/src/test/java/org/glassfish/jersey/tests/e2e/inject/hk2/HK2AbstractBinderInFeaturesTest.java b/tests/e2e-inject/hk2/src/test/java/org/glassfish/jersey/tests/e2e/inject/hk2/HK2AbstractBinderInFeaturesTest.java
new file mode 100644
index 0000000..7a348e2
--- /dev/null
+++ b/tests/e2e-inject/hk2/src/test/java/org/glassfish/jersey/tests/e2e/inject/hk2/HK2AbstractBinderInFeaturesTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.inject.hk2;
+
+import org.glassfish.jersey.internal.inject.AbstractBinder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Feature;
+import javax.ws.rs.core.FeatureContext;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+/**
+ * Test that HK2 binder allows for injection into a feature
+ */
+public class HK2AbstractBinderInFeaturesTest extends JerseyTest {
+
+ private static final AtomicInteger binderCounter = new AtomicInteger();
+ private static final AtomicInteger feature1Counter = new AtomicInteger();
+ private static final AtomicInteger feature2Counter = new AtomicInteger();
+ private static final String VALUE = "CONFIGURED_VALUE";
+
+ public static class InjectableHK2Binder extends org.glassfish.hk2.utilities.binding.AbstractBinder {
+ @Override
+ protected void configure() {
+ binderCounter.incrementAndGet();
+ bindAsContract(ConfigurableInjectable.class).to(Injectable.class).in(Singleton.class);
+ }
+ }
+
+ public static class JerseyInjectableHK2Binder extends AbstractBinder {
+ @Override
+ protected void configure() {
+ bindAsContract(ConfigurableInjectable.class).to(ExtendedInjectable.class).in(Singleton.class);
+ }
+ }
+
+ public static final class InjectableHK2BindingFeature implements Feature {
+ private final Injectable service;
+ private final ExtendedInjectable extendedService;
+
+ @Inject
+ public InjectableHK2BindingFeature(Injectable service, ExtendedInjectable extendedService) {
+ feature1Counter.incrementAndGet();
+ this.service = service;
+ this.extendedService = extendedService;
+ }
+
+ @Override
+ public boolean configure(FeatureContext context) {
+ if (service != null) {
+ ((ConfigurableInjectable) service).set(VALUE);
+ }
+ if (extendedService != null) {
+ feature2Counter.incrementAndGet();
+ }
+ return true;
+ }
+ }
+
+ public static class ConfigurableInjectable implements ExtendedInjectable {
+ private String value;
+ public void set(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+ }
+
+ public static interface ExtendedInjectable extends Injectable {
+ };
+
+ @Override
+ protected Application configure() {
+ return new ResourceConfig(InjectableHK2BindingFeature.class, AbstractBinderTestResource.class,
+ InjectableTestFilter.class, InjectableHK2Binder.class).register(new JerseyInjectableHK2Binder());
+ }
+
+ @Test
+ public void testInjectableInjection() {
+ String response = target().request().get(String.class);
+ assertThat(response, is(VALUE));
+ assertThat(1, is(binderCounter.get()));
+ assertThat(1, is(feature1Counter.get()));
+ assertThat(1, is(feature2Counter.get()));
+ }
+}