Skip to content

Commit e11e086

Browse files
authored
Allow HK2 AbstractBinder class to bind before the Feature is called (#4394)
Signed-off-by: Jan Supol <jan.supol@oracle.com>
1 parent e17b2b1 commit e11e086

File tree

2 files changed

+129
-7
lines changed

2 files changed

+129
-7
lines changed

core-common/src/main/java/org/glassfish/jersey/model/internal/CommonConfig.java

+18-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
33
*
44
* This program and the accompanying materials are made available under the
55
* terms of the Eclipse Public License v. 2.0, which is available at
@@ -28,6 +28,7 @@
2828
import java.util.Map;
2929
import java.util.Set;
3030
import java.util.TreeSet;
31+
import java.util.function.Consumer;
3132
import java.util.function.Function;
3233
import java.util.function.Predicate;
3334
import java.util.logging.Level;
@@ -618,15 +619,19 @@ public void configureAutoDiscoverableProviders(final InjectionManager injectionM
618619
* @param injectionManager injection manager in which the binders and features should be configured.
619620
*/
620621
public void configureMetaProviders(InjectionManager injectionManager, ManagedObjectsFinalizer finalizer) {
622+
final Set<Object> configuredExternals = Collections.newSetFromMap(new IdentityHashMap<>());
623+
621624
// First, configure existing binders
622-
Set<Binder> configuredBinders = configureBinders(injectionManager, Collections.emptySet());
625+
final Set<Binder> configuredBinders = configureBinders(injectionManager, Collections.emptySet());
623626

624627
// Check whether meta providers have been initialized for a config this config has been loaded from.
625628
if (!disableMetaProviderConfiguration) {
629+
// Next, register external meta objects
630+
configureExternalObjects(injectionManager, configuredExternals);
626631
// Configure all features
627632
configureFeatures(injectionManager, new HashSet<>(), resetRegistrations(), finalizer);
628-
// Next, register external meta objects
629-
configureExternalObjects(injectionManager);
633+
// Next, register external meta objects registered by features
634+
configureExternalObjects(injectionManager, configuredExternals);
630635
// At last, configure any new binders added by features
631636
configureBinders(injectionManager, configuredBinders);
632637
}
@@ -653,11 +658,17 @@ private Collection<Binder> getBinder(Set<Binder> configured) {
653658
.collect(Collectors.toList());
654659
}
655660

656-
private void configureExternalObjects(InjectionManager injectionManager) {
661+
private void configureExternalObjects(InjectionManager injectionManager, Set<Object> externalObjects) {
662+
Consumer<Object> registerOnce = o -> {
663+
if (!externalObjects.contains(o)) {
664+
injectionManager.register(o);
665+
externalObjects.add(o);
666+
}
667+
};
657668
componentBag.getInstances(model -> ComponentBag.EXTERNAL_ONLY.test(model, injectionManager))
658-
.forEach(injectionManager::register);
669+
.forEach(registerOnce);
659670
componentBag.getClasses(model -> ComponentBag.EXTERNAL_ONLY.test(model, injectionManager))
660-
.forEach(injectionManager::register);
671+
.forEach(registerOnce);
661672
}
662673

663674
private void configureFeatures(InjectionManager injectionManager,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.tests.e2e.inject.hk2;
18+
19+
import org.glassfish.jersey.internal.inject.AbstractBinder;
20+
import org.glassfish.jersey.server.ResourceConfig;
21+
import org.glassfish.jersey.test.JerseyTest;
22+
import org.junit.Test;
23+
24+
import javax.inject.Inject;
25+
import javax.inject.Singleton;
26+
import javax.ws.rs.core.Application;
27+
import javax.ws.rs.core.Feature;
28+
import javax.ws.rs.core.FeatureContext;
29+
import java.util.concurrent.atomic.AtomicInteger;
30+
31+
import static org.hamcrest.CoreMatchers.is;
32+
import static org.hamcrest.MatcherAssert.assertThat;
33+
34+
/**
35+
* Test that HK2 binder allows for injection into a feature
36+
*/
37+
public class HK2AbstractBinderInFeaturesTest extends JerseyTest {
38+
39+
private static final AtomicInteger binderCounter = new AtomicInteger();
40+
private static final AtomicInteger feature1Counter = new AtomicInteger();
41+
private static final AtomicInteger feature2Counter = new AtomicInteger();
42+
private static final String VALUE = "CONFIGURED_VALUE";
43+
44+
public static class InjectableHK2Binder extends org.glassfish.hk2.utilities.binding.AbstractBinder {
45+
@Override
46+
protected void configure() {
47+
binderCounter.incrementAndGet();
48+
bindAsContract(ConfigurableInjectable.class).to(Injectable.class).in(Singleton.class);
49+
}
50+
}
51+
52+
public static class JerseyInjectableHK2Binder extends AbstractBinder {
53+
@Override
54+
protected void configure() {
55+
bindAsContract(ConfigurableInjectable.class).to(ExtendedInjectable.class).in(Singleton.class);
56+
}
57+
}
58+
59+
public static final class InjectableHK2BindingFeature implements Feature {
60+
private final Injectable service;
61+
private final ExtendedInjectable extendedService;
62+
63+
@Inject
64+
public InjectableHK2BindingFeature(Injectable service, ExtendedInjectable extendedService) {
65+
feature1Counter.incrementAndGet();
66+
this.service = service;
67+
this.extendedService = extendedService;
68+
}
69+
70+
@Override
71+
public boolean configure(FeatureContext context) {
72+
if (service != null) {
73+
((ConfigurableInjectable) service).set(VALUE);
74+
}
75+
if (extendedService != null) {
76+
feature2Counter.incrementAndGet();
77+
}
78+
return true;
79+
}
80+
}
81+
82+
public static class ConfigurableInjectable implements ExtendedInjectable {
83+
private String value;
84+
public void set(String value) {
85+
this.value = value;
86+
}
87+
88+
@Override
89+
public String toString() {
90+
return value;
91+
}
92+
}
93+
94+
public static interface ExtendedInjectable extends Injectable {
95+
};
96+
97+
@Override
98+
protected Application configure() {
99+
return new ResourceConfig(InjectableHK2BindingFeature.class, AbstractBinderTestResource.class,
100+
InjectableTestFilter.class, InjectableHK2Binder.class).register(new JerseyInjectableHK2Binder());
101+
}
102+
103+
@Test
104+
public void testInjectableInjection() {
105+
String response = target().request().get(String.class);
106+
assertThat(response, is(VALUE));
107+
assertThat(1, is(binderCounter.get()));
108+
assertThat(1, is(feature1Counter.get()));
109+
assertThat(1, is(feature2Counter.get()));
110+
}
111+
}

0 commit comments

Comments
 (0)