From deb1b4f26f1f11a7b7b51e488e2d386ce8912e5d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 25 Sep 2025 13:33:48 +0200 Subject: [PATCH 1/3] Prepare issue branch. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 13143c9f6f..a4b789a9c7 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-commons - 4.0.0-SNAPSHOT + 4.0.0-STABLE-VALUE-SNAPSHOT Spring Data Core Core Spring concepts underpinning every Spring Data module. From cd55251dcce0f6f8a8d2bdc5dcc94c9f87a7ad90 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 25 Sep 2025 13:39:40 +0200 Subject: [PATCH 2/3] Use `StableValue` behind `Lazy`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now use Java's StableValue to back our Lazy implementation. StableValue suggests an Optional orElse…(…) usage pattern and it is not primarily intended for lazifying value resolution, therefore we have to mimic StableValue.supplier(…). --- pom.xml | 39 +++++++ .../org/springframework/data/util/Lazy.java | 61 ++--------- .../data/util/LazyDelegate.java | 102 ++++++++++++++++++ .../data/util/LazyDelegate.java | 75 +++++++++++++ 4 files changed, 227 insertions(+), 50 deletions(-) create mode 100644 src/main/java/org/springframework/data/util/LazyDelegate.java create mode 100644 src/main/java25/org/springframework/data/util/LazyDelegate.java diff --git a/pom.xml b/pom.xml index a4b789a9c7..f4bc430605 100644 --- a/pom.xml +++ b/pom.xml @@ -341,18 +341,55 @@ + org.apache.maven.plugins maven-assembly-plugin + org.antora antora-maven-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + default-jar + + + + true + + + + + + + org.apache.maven.plugins maven-compiler-plugin + + + jdk25 + + compile + + + true + 25 + + ${project.basedir}/src/main/java25 + + + true + + + java-test-compile @@ -366,8 +403,10 @@ + + diff --git a/src/main/java/org/springframework/data/util/Lazy.java b/src/main/java/org/springframework/data/util/Lazy.java index 193726da60..f5208ce2c1 100644 --- a/src/main/java/org/springframework/data/util/Lazy.java +++ b/src/main/java/org/springframework/data/util/Lazy.java @@ -22,7 +22,6 @@ import org.jspecify.annotations.Nullable; import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; /** * Simple value type to delay the creation of an object using a {@link Supplier} returning the produced object for @@ -40,30 +39,14 @@ */ public class Lazy implements Supplier { - private static final Lazy EMPTY = new Lazy<>(() -> null, null, true); - static final String UNRESOLVED = "[Unresolved]"; + private static final Lazy EMPTY = new Lazy<>(() -> null); - private final Supplier supplier; + static final String UNRESOLVED = "[Unresolved]"; - private @Nullable T value; - private volatile boolean resolved; + private final LazyDelegate adapter; - private Lazy(Supplier supplier) { - this(supplier, null, false); - } - - /** - * Creates a new {@code Lazy} for the given {@link Supplier}, value and whether it has been resolved or not. - * - * @param supplier must not be {@literal null}. - * @param value can be {@literal null}. - * @param resolved whether the value handed into the constructor represents a resolved value. - */ - private Lazy(Supplier supplier, @Nullable T value, boolean resolved) { - - this.supplier = supplier; - this.value = value; - this.resolved = resolved; + private Lazy(Supplier adapter) { + this.adapter = new LazyDelegate<>(adapter); } /** @@ -128,16 +111,7 @@ public T get() { */ @Nullable public T getNullable() { - - if (resolved) { - return value; - } - - T result = supplier.get(); - this.value = result; - this.resolved = true; - - return result; + return adapter.getNullable(); } /** @@ -249,35 +223,22 @@ public boolean equals(@Nullable Object o) { return false; } - if (resolved != lazy.resolved) { - return false; - } - - if (!ObjectUtils.nullSafeEquals(supplier, lazy.supplier)) { - return false; - } - - return ObjectUtils.nullSafeEquals(value, lazy.value); + return adapter.equals(lazy.adapter); } @Override public int hashCode() { - - int result = ObjectUtils.nullSafeHashCode(supplier); - - result = 31 * result + ObjectUtils.nullSafeHashCode(value); - result = 31 * result + (resolved ? 1 : 0); - - return result; + return adapter.hashCode(); } @Override public String toString() { - if (!resolved) { + if (!adapter.isResolved()) { return UNRESOLVED; } + T value = getNullable(); return value == null ? "null" : value.toString(); } @@ -293,6 +254,6 @@ public String toString(Supplier fallback) { Assert.notNull(fallback, "Fallback must not be null!"); - return resolved ? toString() : fallback.get(); + return adapter.isResolved() ? toString() : fallback.get(); } } diff --git a/src/main/java/org/springframework/data/util/LazyDelegate.java b/src/main/java/org/springframework/data/util/LazyDelegate.java new file mode 100644 index 0000000000..e077b4069c --- /dev/null +++ b/src/main/java/org/springframework/data/util/LazyDelegate.java @@ -0,0 +1,102 @@ +/* + * Copyright 2016-2025 the original author or authors. + * + * 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 + * + * https://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 org.springframework.data.util; + +import java.util.function.Supplier; + +import org.jspecify.annotations.Nullable; + +import org.springframework.util.ObjectUtils; + +/** + * Simple lazy implementation using lazy value resolution. Lazy evaluation not guarded with locks and can therefore lead + * to multiple invocations of the underlying {@link Supplier}. + * + * @author Oliver Gierke + * @author Mark Paluch + * @author Henning Rohlfs + * @author Johannes Englmeier + * @author Greg Turnquist + * @since 4.0 + */ +class LazyDelegate { + + private final Supplier supplier; + + private @Nullable T value; + private volatile boolean resolved; + + LazyDelegate(Supplier supplier) { + this.supplier = supplier; + } + + /** + * Returns the value of the lazy evaluation. + * + * @return the value of the lazy evaluation, can be {@literal null}. + */ + @Nullable + public T getNullable() { + + if (resolved) { + return value; + } + + T result = supplier.get(); + this.value = result; + this.resolved = true; + + return result; + } + + public boolean isResolved() { + return resolved; + } + + @Override + public boolean equals(@Nullable Object o) { + + if (this == o) { + return true; + } + + if (!(o instanceof LazyDelegate lazy)) { + return false; + } + + if (resolved != lazy.resolved) { + return false; + } + + if (!ObjectUtils.nullSafeEquals(supplier, lazy.supplier)) { + return false; + } + + return ObjectUtils.nullSafeEquals(value, lazy.value); + } + + @Override + public int hashCode() { + + int result = ObjectUtils.nullSafeHashCode(supplier); + + result = 31 * result + ObjectUtils.nullSafeHashCode(value); + result = 31 * result + (resolved ? 1 : 0); + + return result; + } + +} diff --git a/src/main/java25/org/springframework/data/util/LazyDelegate.java b/src/main/java25/org/springframework/data/util/LazyDelegate.java new file mode 100644 index 0000000000..73d9fd1ce7 --- /dev/null +++ b/src/main/java25/org/springframework/data/util/LazyDelegate.java @@ -0,0 +1,75 @@ +/* + * Copyright 2016-2025 the original author or authors. + * + * 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 + * + * https://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 org.springframework.data.util; + +import java.util.function.Supplier; + +import org.jspecify.annotations.Nullable; + +import org.springframework.util.ObjectUtils; + +/** + * Lazy adapter using {@link StableValue}. + * + * @author Mark Paluch + * @since 4.0 + */ +class LazyDelegate { + + private final Supplier supplier; + private final StableValue value; + + LazyDelegate(Supplier supplier) { + + System.out.println("Stable"); + this.supplier = supplier; + this.value = StableValue.of(); + } + + /** + * Returns the value of the lazy evaluation. + * + * @return the value of the lazy evaluation, can be {@literal null}. + */ + @Nullable + public T getNullable() { + return value.orElseSet(supplier); + } + + public boolean isResolved() { + return value.isSet(); + } + + @Override + public boolean equals(@Nullable Object o) { + + if (this == o) { + return true; + } + + if (!(o instanceof LazyDelegate lazy)) { + return false; + } + + return ObjectUtils.nullSafeEquals(value, lazy.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} From 86def8328ed238de68edaeca0bc6bae6975c4ba7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 25 Sep 2025 13:42:46 +0200 Subject: [PATCH 3/3] Upgrade build to Java 25. --- ci/pipeline.properties | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ci/pipeline.properties b/ci/pipeline.properties index cde4a8e881..b9297d8d2b 100644 --- a/ci/pipeline.properties +++ b/ci/pipeline.properties @@ -1,10 +1,10 @@ # Java versions -java.main.tag=24.0.1_9-jdk-noble -java.next.tag=24.0.1_9-jdk-noble +java.main.tag=25 +java.next.tag=25-cds # Docker container images - standard -docker.java.main.image=library/eclipse-temurin:${java.main.tag} -docker.java.next.image=library/eclipse-temurin:${java.next.tag} +docker.java.main.image=bellsoft/liberica-openjdk-alpine:${java.main.tag} +docker.java.next.image=bellsoft/liberica-openjdk-alpine:${java.next.tag} # Supported versions of MongoDB docker.mongodb.6.0.version=6.0.23