From fd1c9981e4b9521a827c03767b080d7b999f495b Mon Sep 17 00:00:00 2001 From: Callum Rogers Date: Thu, 12 May 2022 11:53:41 +0100 Subject: [PATCH] Lazily acquire JDK storage locations (#2263) More lazily acquire JDK storage locations --- changelog/@unreleased/pr-2263.v2.yml | 5 + .../baseline/plugins/BaselineIdea.groovy | 4 +- .../plugins/BaselineModuleJvmArgs.java | 1 + .../plugins/BaselineReleaseCompatibility.java | 1 + .../BaselineJavaVersion.java | 8 +- .../BaselineJavaVersionExtension.java | 4 +- .../BaselineJavaVersions.java | 6 +- .../BaselineJavaVersionsExtension.java | 31 +++-- .../plugins/javaversions/JavaToolchains.java | 20 ++-- .../javaversions/LazilyConfiguredMapping.java | 89 +++++++++++++++ .../CheckExplicitSourceCompatibilityTask.java | 4 +- ....palantir.baseline-java-version.properties | 2 +- ...palantir.baseline-java-versions.properties | 2 +- .../BaselineJavaVersionIntegrationTest.groovy | 2 +- .../LazilyConfiguredMappingTest.java | 106 ++++++++++++++++++ 15 files changed, 247 insertions(+), 38 deletions(-) create mode 100644 changelog/@unreleased/pr-2263.v2.yml rename gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/{ => javaversions}/BaselineJavaVersion.java (96%) rename gradle-baseline-java/src/main/groovy/com/palantir/baseline/{extensions => plugins/javaversions}/BaselineJavaVersionExtension.java (95%) rename gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/{ => javaversions}/BaselineJavaVersions.java (96%) rename gradle-baseline-java/src/main/groovy/com/palantir/baseline/{extensions => plugins/javaversions}/BaselineJavaVersionsExtension.java (70%) create mode 100644 gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/LazilyConfiguredMapping.java create mode 100644 gradle-baseline-java/src/test/groovy/com/palantir/baseline/plugins/javaversions/LazilyConfiguredMappingTest.java diff --git a/changelog/@unreleased/pr-2263.v2.yml b/changelog/@unreleased/pr-2263.v2.yml new file mode 100644 index 000000000..fdd0117ae --- /dev/null +++ b/changelog/@unreleased/pr-2263.v2.yml @@ -0,0 +1,5 @@ +type: fix +fix: + description: More lazily acquire JDK storage locations + links: + - https://github.com/palantir/gradle-baseline/pull/2263 diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineIdea.groovy b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineIdea.groovy index 62933c5c1..e4a56ed04 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineIdea.groovy +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineIdea.groovy @@ -18,8 +18,8 @@ package com.palantir.baseline.plugins import com.google.common.collect.ImmutableMap import com.palantir.baseline.IntellijSupport -import com.palantir.baseline.extensions.BaselineJavaVersionExtension -import com.palantir.baseline.extensions.BaselineJavaVersionsExtension +import com.palantir.baseline.plugins.javaversions.BaselineJavaVersionExtension +import com.palantir.baseline.plugins.javaversions.BaselineJavaVersionsExtension import com.palantir.baseline.util.GitUtils import groovy.transform.CompileStatic import groovy.xml.XmlUtil diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineModuleJvmArgs.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineModuleJvmArgs.java index 0c1d53d6b..cc81e784a 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineModuleJvmArgs.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineModuleJvmArgs.java @@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.palantir.baseline.extensions.BaselineModuleJvmArgsExtension; +import com.palantir.baseline.plugins.javaversions.BaselineJavaVersion; import java.io.IOException; import java.util.List; import java.util.Objects; diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineReleaseCompatibility.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineReleaseCompatibility.java index 58c55c323..a62b1c9a8 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineReleaseCompatibility.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineReleaseCompatibility.java @@ -17,6 +17,7 @@ package com.palantir.baseline.plugins; import com.google.common.collect.ImmutableList; +import com.palantir.baseline.plugins.javaversions.BaselineJavaVersion; import java.util.Collections; import java.util.Optional; import org.gradle.api.JavaVersion; diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineJavaVersion.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/BaselineJavaVersion.java similarity index 96% rename from gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineJavaVersion.java rename to gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/BaselineJavaVersion.java index 30c47036c..e17974e7e 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineJavaVersion.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/BaselineJavaVersion.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2021 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2022 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,8 @@ * limitations under the License. */ -package com.palantir.baseline.plugins; +package com.palantir.baseline.plugins.javaversions; -import com.palantir.baseline.extensions.BaselineJavaVersionExtension; -import com.palantir.baseline.extensions.BaselineJavaVersionsExtension; -import com.palantir.baseline.plugins.javaversions.BaselineJavaToolchain; -import com.palantir.baseline.plugins.javaversions.JavaToolchains; import javax.inject.Inject; import org.gradle.api.Action; import org.gradle.api.DefaultTask; diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/extensions/BaselineJavaVersionExtension.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/BaselineJavaVersionExtension.java similarity index 95% rename from gradle-baseline-java/src/main/groovy/com/palantir/baseline/extensions/BaselineJavaVersionExtension.java rename to gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/BaselineJavaVersionExtension.java index 82393cbbe..162b17cfd 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/extensions/BaselineJavaVersionExtension.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/BaselineJavaVersionExtension.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2022 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.palantir.baseline.extensions; +package com.palantir.baseline.plugins.javaversions; import javax.inject.Inject; import org.gradle.api.Project; diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineJavaVersions.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/BaselineJavaVersions.java similarity index 96% rename from gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineJavaVersions.java rename to gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/BaselineJavaVersions.java index cd5856afb..a46edd039 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/BaselineJavaVersions.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/BaselineJavaVersions.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2021 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2022 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,10 @@ * limitations under the License. */ -package com.palantir.baseline.plugins; +package com.palantir.baseline.plugins.javaversions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import com.palantir.baseline.extensions.BaselineJavaVersionExtension; -import com.palantir.baseline.extensions.BaselineJavaVersionsExtension; import java.util.Objects; import org.gradle.api.GradleException; import org.gradle.api.Named; diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/extensions/BaselineJavaVersionsExtension.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/BaselineJavaVersionsExtension.java similarity index 70% rename from gradle-baseline-java/src/main/groovy/com/palantir/baseline/extensions/BaselineJavaVersionsExtension.java rename to gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/BaselineJavaVersionsExtension.java index bdb4aca4b..f893893c7 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/extensions/BaselineJavaVersionsExtension.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/BaselineJavaVersionsExtension.java @@ -1,5 +1,5 @@ /* - * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved. + * (c) Copyright 2022 Palantir Technologies Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,12 @@ * limitations under the License. */ -package com.palantir.baseline.extensions; +package com.palantir.baseline.plugins.javaversions; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; import javax.inject.Inject; import org.gradle.api.Project; -import org.gradle.api.provider.MapProperty; import org.gradle.api.provider.Property; import org.gradle.jvm.toolchain.JavaInstallationMetadata; import org.gradle.jvm.toolchain.JavaLanguageVersion; @@ -31,7 +32,8 @@ public class BaselineJavaVersionsExtension { private final Property libraryTarget; private final Property distributionTarget; private final Property runtime; - private final MapProperty jdks; + private final LazilyConfiguredMapping, Project> + jdks = new LazilyConfiguredMapping<>(AtomicReference::new); @Inject public BaselineJavaVersionsExtension(Project project) { @@ -45,9 +47,6 @@ public BaselineJavaVersionsExtension(Project project) { libraryTarget.finalizeValueOnRead(); distributionTarget.finalizeValueOnRead(); runtime.finalizeValueOnRead(); - - jdks = project.getObjects().mapProperty(JavaLanguageVersion.class, JavaInstallationMetadata.class); - jdks.finalizeValueOnRead(); } /** Target {@link JavaLanguageVersion} for compilation of libraries that are published. */ @@ -80,7 +79,21 @@ public final void setRuntime(int value) { runtime.set(JavaLanguageVersion.of(value)); } - public final MapProperty getJdks() { - return jdks; + public final Optional jdkMetadataFor( + JavaLanguageVersion javaLanguageVersion, Project project) { + return jdks.get(javaLanguageVersion, project).map(AtomicReference::get); + } + + public final void jdk(JavaLanguageVersion javaLanguageVersion, JavaInstallationMetadata javaInstallationMetadata) { + jdks.put(javaLanguageVersion, ref -> ref.set(javaInstallationMetadata)); + } + + public final void jdks(LazyJdks lazyJdks) { + jdks.put((javaLanguageVersion, project) -> lazyJdks.jdkFor(javaLanguageVersion, project) + .map(javaInstallationMetadata -> ref -> ref.set(javaInstallationMetadata))); + } + + public interface LazyJdks { + Optional jdkFor(JavaLanguageVersion javaLanguageVersion, Project project); } } diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/JavaToolchains.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/JavaToolchains.java index 12f203539..4afcf46cc 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/JavaToolchains.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/JavaToolchains.java @@ -16,7 +16,6 @@ package com.palantir.baseline.plugins.javaversions; -import com.palantir.baseline.extensions.BaselineJavaVersionsExtension; import org.gradle.api.Project; import org.gradle.api.provider.Provider; import org.gradle.jvm.toolchain.JavaInstallationMetadata; @@ -34,15 +33,16 @@ public JavaToolchains(Project project, BaselineJavaVersionsExtension baselineJav public Provider forVersion(Provider javaLanguageVersionProvider) { return javaLanguageVersionProvider.map(javaLanguageVersion -> { - Provider configuredJdkMetadata = baselineJavaVersionsExtension - .getJdks() - .getting(javaLanguageVersion) - .orElse(project.provider(() -> project.getExtensions() - .getByType(JavaToolchainService.class) - .launcherFor(javaToolchainSpec -> - javaToolchainSpec.getLanguageVersion().set(javaLanguageVersion)) - .get() - .getMetadata())); + Provider configuredJdkMetadata = + project.provider(() -> baselineJavaVersionsExtension + .jdkMetadataFor(javaLanguageVersion, project) + .orElseGet(() -> project.getExtensions() + .getByType(JavaToolchainService.class) + .launcherFor(javaToolchainSpec -> javaToolchainSpec + .getLanguageVersion() + .set(javaLanguageVersion)) + .get() + .getMetadata())); return new ConfiguredJavaToolchain( project.getObjects(), diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/LazilyConfiguredMapping.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/LazilyConfiguredMapping.java new file mode 100644 index 000000000..647932cc4 --- /dev/null +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/plugins/javaversions/LazilyConfiguredMapping.java @@ -0,0 +1,89 @@ +/* + * (c) Copyright 2022 Palantir Technologies Inc. All rights reserved. + * + * 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 com.palantir.baseline.plugins.javaversions; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; +import org.gradle.api.Action; + +final class LazilyConfiguredMapping { + private final Supplier valueFactory; + private final List> values = new ArrayList<>(); + private final Map> computedValues = new HashMap<>(); + private boolean finalized = false; + + LazilyConfiguredMapping(Supplier valueFactory) { + this.valueFactory = valueFactory; + } + + public void put(LazyValues lazyValues) { + ensureNotFinalized(); + + values.add(lazyValues); + } + + public void put(K key, Action value) { + ensureNotFinalized(); + + put((requestedKey, _ignored) -> { + if (requestedKey.equals(key)) { + return Optional.of(value); + } + + return Optional.empty(); + }); + } + + private void ensureNotFinalized() { + if (finalized) { + throw new IllegalStateException(String.format( + "This %s has already been finalized as get() hase been called. " + + "No further elements can be added to it", + LazilyConfiguredMapping.class.getSimpleName())); + } + } + + public Optional get(K key, A additionalData) { + finalized = true; + + return computedValues.computeIfAbsent(key, _ignored -> { + V value = valueFactory.get(); + AtomicBoolean created = new AtomicBoolean(false); + values.forEach(lazyValues -> { + lazyValues.compute(key, additionalData).ifPresent(action -> { + created.set(true); + action.execute(value); + }); + }); + + if (created.get()) { + return Optional.of(value); + } + + return Optional.empty(); + }); + } + + public interface LazyValues { + Optional> compute(K key, A additionalData); + } +} diff --git a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/tasks/CheckExplicitSourceCompatibilityTask.java b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/tasks/CheckExplicitSourceCompatibilityTask.java index 688b9ae12..38a6384e1 100644 --- a/gradle-baseline-java/src/main/groovy/com/palantir/baseline/tasks/CheckExplicitSourceCompatibilityTask.java +++ b/gradle-baseline-java/src/main/groovy/com/palantir/baseline/tasks/CheckExplicitSourceCompatibilityTask.java @@ -16,7 +16,7 @@ package com.palantir.baseline.tasks; -import com.palantir.baseline.plugins.BaselineJavaVersion; +import com.palantir.baseline.plugins.javaversions.BaselineJavaVersion; import java.io.IOException; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; @@ -43,7 +43,7 @@ /** * By default, Gradle will infer sourceCompat based on whatever JVM is currently being used to evaluate the * build.gradle files. This is bad for reproducibility because if we make an automated PR to upgrade the Java major - * version (e.g. 11 -> 15) then a library might unintentionally start publishing jars containing Java15 bytecode! + * version (e.g. 11 to 15) then a library might unintentionally start publishing jars containing Java15 bytecode! * * Better to just require everyone to specify sourceCompatibility explicitly! */ diff --git a/gradle-baseline-java/src/main/resources/META-INF/gradle-plugins/com.palantir.baseline-java-version.properties b/gradle-baseline-java/src/main/resources/META-INF/gradle-plugins/com.palantir.baseline-java-version.properties index bda4d2359..fd6b289da 100644 --- a/gradle-baseline-java/src/main/resources/META-INF/gradle-plugins/com.palantir.baseline-java-version.properties +++ b/gradle-baseline-java/src/main/resources/META-INF/gradle-plugins/com.palantir.baseline-java-version.properties @@ -1 +1 @@ -implementation-class=com.palantir.baseline.plugins.BaselineJavaVersion +implementation-class=com.palantir.baseline.plugins.javaversions.BaselineJavaVersion diff --git a/gradle-baseline-java/src/main/resources/META-INF/gradle-plugins/com.palantir.baseline-java-versions.properties b/gradle-baseline-java/src/main/resources/META-INF/gradle-plugins/com.palantir.baseline-java-versions.properties index 8d4e15fbe..daf189e5a 100644 --- a/gradle-baseline-java/src/main/resources/META-INF/gradle-plugins/com.palantir.baseline-java-versions.properties +++ b/gradle-baseline-java/src/main/resources/META-INF/gradle-plugins/com.palantir.baseline-java-versions.properties @@ -1 +1 @@ -implementation-class=com.palantir.baseline.plugins.BaselineJavaVersions +implementation-class=com.palantir.baseline.plugins.javaversions.BaselineJavaVersions diff --git a/gradle-baseline-java/src/test/groovy/com/palantir/baseline/BaselineJavaVersionIntegrationTest.groovy b/gradle-baseline-java/src/test/groovy/com/palantir/baseline/BaselineJavaVersionIntegrationTest.groovy index 740ce2660..3b21868fe 100644 --- a/gradle-baseline-java/src/test/groovy/com/palantir/baseline/BaselineJavaVersionIntegrationTest.groovy +++ b/gradle-baseline-java/src/test/groovy/com/palantir/baseline/BaselineJavaVersionIntegrationTest.groovy @@ -302,7 +302,7 @@ class BaselineJavaVersionIntegrationTest extends IntegrationSpec { javaVersions { libraryTarget = 11 - jdks.put JavaLanguageVersion.of(11), new JavaInstallationMetadata() { + jdk JavaLanguageVersion.of(11), new JavaInstallationMetadata() { @Override JavaLanguageVersion getLanguageVersion() { return JavaLanguageVersion.of(11) diff --git a/gradle-baseline-java/src/test/groovy/com/palantir/baseline/plugins/javaversions/LazilyConfiguredMappingTest.java b/gradle-baseline-java/src/test/groovy/com/palantir/baseline/plugins/javaversions/LazilyConfiguredMappingTest.java new file mode 100644 index 000000000..82fa86c9a --- /dev/null +++ b/gradle-baseline-java/src/test/groovy/com/palantir/baseline/plugins/javaversions/LazilyConfiguredMappingTest.java @@ -0,0 +1,106 @@ +/* + * (c) Copyright 2022 Palantir Technologies Inc. All rights reserved. + * + * 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 com.palantir.baseline.plugins.javaversions; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Optional; +import org.junit.jupiter.api.Test; + +class LazilyConfiguredMappingTest { + private final LazilyConfiguredMapping lazilyConfiguredMapping = + new LazilyConfiguredMapping<>(() -> new Extension(0)); + + @Test + void empty_mapping_returns_optional_empty() { + assertThat(lazilyConfiguredMapping.get("abc", 'a')).isEmpty(); + } + + @Test + void can_put_a_value_and_get_it_out_again() { + lazilyConfiguredMapping.put("foo", extension -> extension.number = 4); + + assertThat(lazilyConfiguredMapping.get("foo", 'a')).hasValue(new Extension(4)); + } + + @Test + void can_put_a_lazy_value_in_and_get_it_out_again() { + lazilyConfiguredMapping.put((key, additionalData) -> { + assertThat(additionalData).isEqualTo('b'); + return Optional.of(extension -> extension.number = Integer.parseInt(key)); + }); + + assertThat(lazilyConfiguredMapping.get("3", 'b')).hasValue(new Extension(3)); + assertThat(lazilyConfiguredMapping.get("9", 'b')).hasValue(new Extension(9)); + } + + @Test + void lazy_values_are_able_to_not_return_values() { + lazilyConfiguredMapping.put((_key, _additionalData) -> Optional.empty()); + + assertThat(lazilyConfiguredMapping.get("abc", 'a')).isEmpty(); + } + + @Test + void interspersing_putting_values_takes_the_last_set_value() { + lazilyConfiguredMapping.put("1", extension -> extension.number = 80); + lazilyConfiguredMapping.put( + (key, _ignored) -> Optional.of(extension -> extension.number = Integer.parseInt(key))); + lazilyConfiguredMapping.put("4", extension -> extension.number = 99); + + assertThat(lazilyConfiguredMapping.get("1", 'c')).hasValue(new Extension(1)); + assertThat(lazilyConfiguredMapping.get("3", 'c')).hasValue(new Extension(3)); + assertThat(lazilyConfiguredMapping.get("4", 'c')).hasValue(new Extension(99)); + } + + @Test + void throws_if_putting_values_after_being_finalized() { + lazilyConfiguredMapping.get("abc", 'c'); + + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> { + lazilyConfiguredMapping.put("foo", extension -> {}); + }); + + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> { + lazilyConfiguredMapping.put((_key, _additionalData) -> Optional.of(extension -> {})); + }); + } + + private static final class Extension { + public int number; + + Extension(int number) { + this.number = number; + } + + @Override + public boolean equals(Object obj) { + return number == ((Extension) obj).number; + } + + @Override + public int hashCode() { + return number; + } + + @Override + public String toString() { + return Integer.toString(number); + } + } +}