Skip to content

Commit

Permalink
Allow empty list injection (#7160)
Browse files Browse the repository at this point in the history
* fix for issue #7082
  • Loading branch information
trentjeff authored Jul 10, 2023
1 parent d35b0a7 commit 1628a07
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@
import io.helidon.common.config.GlobalConfig;
import io.helidon.config.spi.ConfigSource;
import io.helidon.pico.api.ExternalContracts;
import io.helidon.pico.api.PicoServices;
import io.helidon.pico.api.ServiceInfoCriteria;
import io.helidon.pico.api.ServiceProvider;

import jakarta.inject.Inject;
import jakarta.inject.Provider;
Expand All @@ -40,13 +37,7 @@ class ConfigProducer implements Config {
private final Config config;

@Inject
ConfigProducer() {
// this should be moved to constructor injection when we support zero length lists for injection
List<ServiceProvider<?>> serviceProviders = PicoServices.realizedServices()
.lookupAll(ServiceInfoCriteria
.builder()
.addContractImplemented(ConfigSource.class).build(), false);

ConfigProducer(List<Provider<ConfigSource>> serviceProviders) {
if (GlobalConfig.configured()) {
config = GlobalConfig.config();
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Binder bind(String id,
* It is assumed that the caller of this is aware of the proper cardinality for each injection point.
*
* @param id the injection point identity
* @param serviceProviders the list of service providers to bind to this identity.
* @param serviceProviders the list of service providers to bind to this identity
* @return the binder builder
*/
Binder bindMany(String id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,11 @@ private static boolean isSelf(ServiceProvider<?> self,
}

private static boolean allowNullableInjectionPoint(InjectionPointInfo ipInfo) {
if (ipInfo.listWrapped()) {
// allow empty lists to be injected
return true;
}

ServiceInfoCriteria missingServiceInfo = ipInfo.dependencyToServiceInfo();
Set<TypeName> contractsNeeded = missingServiceInfo.contractsImplemented();
return (1 == contractsNeeded.size() && contractsNeeded.contains(INTERCEPTOR));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.helidon.pico.tests.pico;

import io.helidon.pico.api.Contract;

@Contract
interface AContractWithNoServiceImplementations {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.helidon.pico.tests.pico;

import java.util.List;

import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.inject.Singleton;

@Singleton
class AServiceUsingAContractWithNoServiceImplementations {

@Inject
AServiceUsingAContractWithNoServiceImplementations(List<Provider<AContractWithNoServiceImplementations>> list) {
if (!list.isEmpty()) {
throw new AssertionError("Should be empty: " + list);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.helidon.pico.tests.pico;

import io.helidon.config.Config;
import io.helidon.pico.api.ServiceProvider;
import io.helidon.pico.api.Services;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static io.helidon.pico.testing.PicoTestingSupport.basicTestableConfig;
import static io.helidon.pico.testing.PicoTestingSupport.resetAll;
import static io.helidon.pico.testing.PicoTestingSupport.testableServices;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;

class EmptyListInjectionTest {
private static final Config CONFIG = basicTestableConfig();

private Services services;

@BeforeEach
void setUp() {
this.services = testableServices(CONFIG).services();
}

@AfterEach
void tearDown() {
resetAll();
}

@Test
void acceptEmptyListInjectables() {
ServiceProvider<AServiceUsingAContractWithNoServiceImplementations> sp =
services.lookupFirst(AServiceUsingAContractWithNoServiceImplementations.class);
assertThat(sp.get(), notNullValue());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -348,15 +348,15 @@ List<String> toInjectionPlanBindings(ServiceProvider<?> sp) {
List<?> ipUnqualified = e.getValue().unqualifiedProviders();
boolean resolved = false;
try {
if (ipQualified.isEmpty()) {
if (ipInfo.listWrapped()) {
line.append(".bindMany(");
} else if (ipQualified.isEmpty()) {
if (!ipUnqualified.isEmpty()) {
resolved = true;
line.append(".resolvedBind(");
} else {
line.append(".bindVoid(");
}
} else if (ipInfo.listWrapped()) {
line.append(".bindMany(");
} else {
line.append(".bind(");
}
Expand Down

0 comments on commit 1628a07

Please sign in to comment.