From 7ea18ceb89f9bb6211782147dd5d90e9c4d46fb4 Mon Sep 17 00:00:00 2001 From: Christopher Chianelli Date: Wed, 15 Apr 2026 10:34:20 -0400 Subject: [PATCH 1/5] feat: automatically enable automatic node sharing when possible --- .../score/director/AutomaticNodeSharing.java | 7 ++++ .../director/ScoreDirectorFactoryConfig.java | 8 ++--- ...tConstraintStreamScoreDirectorFactory.java | 23 +++++++++---- core/src/main/resources/solver.xsd | 16 +++++++++- .../quarkus/deployment/TimefoldProcessor.java | 22 +++---------- .../config/SolverBuildTimeConfig.java | 3 +- ...efoldProcessorNodeSharingFailFastTest.java | 24 +++++++------- .../quarkus/solverConfigWithNodeSharing.xml | 2 +- .../TimefoldSolverAutoConfiguration.java | 32 +++++++++++++++---- .../config/SolverProperties.java | 9 +++--- .../autoconfigure/config/SolverProperty.java | 3 +- ...SolverConstraintAutoConfigurationTest.java | 4 +-- .../src/main/resources/benchmark.xsd | 23 ++++++++++++- 13 files changed, 118 insertions(+), 58 deletions(-) create mode 100644 core/src/main/java/ai/timefold/solver/core/config/score/director/AutomaticNodeSharing.java diff --git a/core/src/main/java/ai/timefold/solver/core/config/score/director/AutomaticNodeSharing.java b/core/src/main/java/ai/timefold/solver/core/config/score/director/AutomaticNodeSharing.java new file mode 100644 index 00000000000..f44f681a69d --- /dev/null +++ b/core/src/main/java/ai/timefold/solver/core/config/score/director/AutomaticNodeSharing.java @@ -0,0 +1,7 @@ +package ai.timefold.solver.core.config.score.director; + +public enum AutomaticNodeSharing { + ON, + OFF, + AUTO; +} diff --git a/core/src/main/java/ai/timefold/solver/core/config/score/director/ScoreDirectorFactoryConfig.java b/core/src/main/java/ai/timefold/solver/core/config/score/director/ScoreDirectorFactoryConfig.java index abb78182010..78c6202cbc8 100644 --- a/core/src/main/java/ai/timefold/solver/core/config/score/director/ScoreDirectorFactoryConfig.java +++ b/core/src/main/java/ai/timefold/solver/core/config/score/director/ScoreDirectorFactoryConfig.java @@ -40,7 +40,7 @@ public final class ScoreDirectorFactoryConfig extends AbstractConfig constraintProviderCustomProperties = null; - private Boolean constraintStreamAutomaticNodeSharing; + private AutomaticNodeSharing constraintStreamAutomaticNodeSharing; private Boolean constraintStreamProfilingEnabled; private Class incrementalScoreCalculatorClass = null; @@ -92,11 +92,11 @@ public void setConstraintProviderCustomProperties( this.constraintProviderCustomProperties = constraintProviderCustomProperties; } - public @Nullable Boolean getConstraintStreamAutomaticNodeSharing() { + public @Nullable AutomaticNodeSharing getConstraintStreamAutomaticNodeSharing() { return constraintStreamAutomaticNodeSharing; } - public void setConstraintStreamAutomaticNodeSharing(@Nullable Boolean constraintStreamAutomaticNodeSharing) { + public void setConstraintStreamAutomaticNodeSharing(@Nullable AutomaticNodeSharing constraintStreamAutomaticNodeSharing) { this.constraintStreamAutomaticNodeSharing = constraintStreamAutomaticNodeSharing; } @@ -193,7 +193,7 @@ public void setAssertionScoreDirectorFactory(@Nullable ScoreDirectorFactoryConfi } public @NonNull ScoreDirectorFactoryConfig - withConstraintStreamAutomaticNodeSharing(@NonNull Boolean constraintStreamAutomaticNodeSharing) { + withConstraintStreamAutomaticNodeSharing(@NonNull AutomaticNodeSharing constraintStreamAutomaticNodeSharing) { this.constraintStreamAutomaticNodeSharing = constraintStreamAutomaticNodeSharing; return this; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java index df7939d3983..a8c896a6a5d 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java @@ -7,6 +7,7 @@ import ai.timefold.solver.core.api.score.Score; import ai.timefold.solver.core.api.score.stream.ConstraintMetaModel; import ai.timefold.solver.core.api.score.stream.ConstraintProvider; +import ai.timefold.solver.core.config.score.director.AutomaticNodeSharing; import ai.timefold.solver.core.config.score.director.ScoreDirectorFactoryConfig; import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.config.util.ConfigUtils; @@ -47,13 +48,21 @@ public final class BavetConstraintStreamScoreDirectorFactory getConstraintProviderClass(ScoreDirectorFactoryConfig config, Class providedConstraintProviderClass) { - if (Boolean.TRUE.equals(config.getConstraintStreamAutomaticNodeSharing())) { - var enterpriseService = - TimefoldSolverEnterpriseService.loadOrFail(TimefoldSolverEnterpriseService.Feature.AUTOMATIC_NODE_SHARING); - return enterpriseService.createNodeSharer().buildNodeSharedConstraintProvider(providedConstraintProviderClass); - } else { - return providedConstraintProviderClass; - } + var automaticNodeSharing = Objects.requireNonNullElse(config.getConstraintStreamAutomaticNodeSharing(), + AutomaticNodeSharing.AUTO); + return switch (automaticNodeSharing) { + case OFF -> providedConstraintProviderClass; + case ON -> { + var enterpriseService = + TimefoldSolverEnterpriseService + .loadOrFail(TimefoldSolverEnterpriseService.Feature.AUTOMATIC_NODE_SHARING); + yield enterpriseService.createNodeSharer().buildNodeSharedConstraintProvider(providedConstraintProviderClass); + } + case AUTO -> TimefoldSolverEnterpriseService.loadOrDefault( + enterpriseService -> enterpriseService.createNodeSharer() + .buildNodeSharedConstraintProvider(providedConstraintProviderClass), + () -> providedConstraintProviderClass); + }; } private final BavetConstraintSessionFactory constraintSessionFactory; diff --git a/core/src/main/resources/solver.xsd b/core/src/main/resources/solver.xsd index 8e4c489f223..8562561fc0e 100644 --- a/core/src/main/resources/solver.xsd +++ b/core/src/main/resources/solver.xsd @@ -103,7 +103,7 @@ - + @@ -1539,6 +1539,20 @@ + + + + + + + + + + + + + + diff --git a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/TimefoldProcessor.java b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/TimefoldProcessor.java index cb846924629..50f11a9294d 100644 --- a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/TimefoldProcessor.java +++ b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/TimefoldProcessor.java @@ -260,7 +260,6 @@ SolverConfigBuildItem recordAndRegisterBuildTimeBeans(CombinedIndexBuildItem com // Step 2 - validate all SolverConfig definitions assertNoMemberAnnotationWithoutClassAnnotation(indexView); - assertNodeSharingDisabled(solverConfigMap); assertSolverConfigSolutionClasses(indexView, solverConfigMap); assertSolverConfigEntityClasses(indexView); assertSolverConfigConstraintClasses(indexView, solverConfigMap); @@ -388,22 +387,6 @@ Some solver configs (%s) don't specify a %s class, yet there are multiple availa } } - private void assertNodeSharingDisabled(Map solverConfigMap) { - for (var entry : solverConfigMap.entrySet()) { - var solverConfig = entry.getValue(); - var scoreDirectorFactoryConfig = solverConfig.getScoreDirectorFactoryConfig(); - if (scoreDirectorFactoryConfig != null && - Boolean.TRUE.equals(scoreDirectorFactoryConfig.getConstraintStreamAutomaticNodeSharing())) { - throw new IllegalStateException(""" - SolverConfig %s enabled automatic node sharing via SolverConfig, which is not allowed. - Enable automatic node sharing with the property %s instead.""" - .formatted( - entry.getKey(), - "quarkus.timefold.solver.constraint-stream-automatic-node-sharing=true")); - } - } - } - private void assertSolverConfigEntityClasses(IndexView indexView) { // No entity classes assertEmptyInstances(indexView, DotNames.PLANNING_ENTITY); @@ -813,6 +796,11 @@ private void applySolverProperties(IndexView indexView, String solverName, Solve } solverConfig.withNearbyDistanceMeterClass((Class>) clazz); }); + + timefoldBuildTimeConfig.getSolverConfig(solverName) + .flatMap(SolverBuildTimeConfig::constraintStreamAutomaticNodeSharing) + .ifPresent(automaticNodeSharing -> solverConfig.getScoreDirectorFactoryConfig() + .withConstraintStreamAutomaticNodeSharing(automaticNodeSharing)); // Termination properties are set at runtime } diff --git a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java index 954a044cf7d..2068cd7cfc7 100644 --- a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java +++ b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java @@ -4,6 +4,7 @@ import java.util.Set; import ai.timefold.solver.core.api.score.stream.ConstraintProvider; +import ai.timefold.solver.core.config.score.director.AutomaticNodeSharing; import ai.timefold.solver.core.config.solver.PreviewFeature; import ai.timefold.solver.core.config.solver.SolverConfig; import ai.timefold.solver.quarkus.config.SolverRuntimeConfig; @@ -63,5 +64,5 @@ public interface SolverBuildTimeConfig { * Enterprise Edition. */ // Build time - modifies the ConstraintProvider class if set - Optional constraintStreamAutomaticNodeSharing(); + Optional constraintStreamAutomaticNodeSharing(); } diff --git a/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java b/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java index f2c5d3f1d69..94c432a2a4b 100644 --- a/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java +++ b/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java @@ -1,8 +1,11 @@ package ai.timefold.solver.quarkus; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import jakarta.inject.Inject; + +import ai.timefold.solver.core.config.score.director.AutomaticNodeSharing; +import ai.timefold.solver.core.config.solver.SolverConfig; import ai.timefold.solver.quarkus.testdomain.normal.TestdataQuarkusConstraintProvider; import ai.timefold.solver.quarkus.testdomain.normal.TestdataQuarkusEntity; import ai.timefold.solver.quarkus.testdomain.normal.TestdataQuarkusSolution; @@ -23,18 +26,15 @@ public class TimefoldProcessorNodeSharingFailFastTest { .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) .addClasses(TestdataQuarkusEntity.class, TestdataQuarkusSolution.class, TestdataQuarkusConstraintProvider.class) - .addAsResource("ai/timefold/solver/quarkus/solverConfigWithNodeSharing.xml")) - .assertException(exception -> { - assertThat(exception).isInstanceOf(IllegalStateException.class) - .hasMessageContainingAll( - "enabled automatic node sharing via SolverConfig, which is not allowed.", - "Enable automatic node sharing with the property", - "quarkus.timefold.solver.constraint-stream-automatic-node-sharing=true"); - }); + .addAsResource("ai/timefold/solver/quarkus/solverConfigWithNodeSharing.xml")); + + @Inject + SolverConfig solverConfig; @Test - void test() { - fail("Should not call this method."); + void isEnabledInSolverConfig() { + assertEquals(AutomaticNodeSharing.ON, + solverConfig.getScoreDirectorFactoryConfig().getConstraintStreamAutomaticNodeSharing()); } } diff --git a/quarkus-integration/quarkus/deployment/src/test/resources/ai/timefold/solver/quarkus/solverConfigWithNodeSharing.xml b/quarkus-integration/quarkus/deployment/src/test/resources/ai/timefold/solver/quarkus/solverConfigWithNodeSharing.xml index 254768a46a5..414575bb6d0 100644 --- a/quarkus-integration/quarkus/deployment/src/test/resources/ai/timefold/solver/quarkus/solverConfigWithNodeSharing.xml +++ b/quarkus-integration/quarkus/deployment/src/test/resources/ai/timefold/solver/quarkus/solverConfigWithNodeSharing.xml @@ -2,6 +2,6 @@ - true + ON diff --git a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverAutoConfiguration.java b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverAutoConfiguration.java index 026efca83f9..047e11a71ae 100644 --- a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverAutoConfiguration.java +++ b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverAutoConfiguration.java @@ -31,6 +31,7 @@ import ai.timefold.solver.core.api.solver.SolutionManager; import ai.timefold.solver.core.api.solver.SolverFactory; import ai.timefold.solver.core.api.solver.SolverManager; +import ai.timefold.solver.core.config.score.director.AutomaticNodeSharing; import ai.timefold.solver.core.config.score.director.ScoreDirectorFactoryConfig; import ai.timefold.solver.core.config.solver.SolverConfig; import ai.timefold.solver.core.config.solver.termination.DiminishedReturnsTerminationConfig; @@ -261,14 +262,31 @@ private void loadSolverConfig(IncludeAbstractClassesEntityScanner entityScanner, private void applyScoreDirectorFactoryProperties(IncludeAbstractClassesEntityScanner entityScanner, SolverConfig solverConfig, SolverProperties solverProperties) { applyScoreDirectorFactoryProperties(entityScanner, solverConfig); - if (solverProperties.getConstraintStreamAutomaticNodeSharing() != null - && solverProperties.getConstraintStreamAutomaticNodeSharing()) { - if (NativeDetector.inNativeImage()) { - throw new UnsupportedOperationException( - "Constraint stream automatic node sharing is unsupported in a Spring native image."); - } + if (solverProperties.getConstraintStreamAutomaticNodeSharing() != null) { + Objects.requireNonNull(solverConfig.getScoreDirectorFactoryConfig()) + .setConstraintStreamAutomaticNodeSharing(switch (Objects.requireNonNullElse( + solverProperties.getConstraintStreamAutomaticNodeSharing(), + AutomaticNodeSharing.AUTO)) { + case ON -> { + if (NativeDetector.inNativeImage()) { + throw new UnsupportedOperationException( + "Constraint stream automatic node sharing is unsupported in a Spring native image."); + } + yield AutomaticNodeSharing.ON; + } + case OFF -> AutomaticNodeSharing.OFF; + case AUTO -> { + if (NativeDetector.inNativeImage()) { + yield AutomaticNodeSharing.OFF; + } + yield AutomaticNodeSharing.AUTO; + } + }); + } else if (NativeDetector.inNativeImage()) { + // Explicitly set it to disabled in a native image if unspecified so + // the solver does not try to node share when enterprise is used. Objects.requireNonNull(solverConfig.getScoreDirectorFactoryConfig()) - .setConstraintStreamAutomaticNodeSharing(true); + .setConstraintStreamAutomaticNodeSharing(AutomaticNodeSharing.OFF); } if (solverProperties.getConstraintStreamProfilingEnabled() != null) { Objects.requireNonNull(solverConfig.getScoreDirectorFactoryConfig()) diff --git a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java index 028006d5d5c..3a3a4a09627 100644 --- a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java +++ b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java @@ -5,6 +5,7 @@ import java.util.TreeSet; import ai.timefold.solver.core.api.score.stream.ConstraintProvider; +import ai.timefold.solver.core.config.score.director.AutomaticNodeSharing; import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.config.solver.PreviewFeature; import ai.timefold.solver.core.impl.heuristic.selector.common.nearby.NearbyDistanceMeter; @@ -56,9 +57,9 @@ public class SolverProperties { * so nodes share lambdas when possible, improving performance. * When enabled, breakpoints placed in the {@link ConstraintProvider} * will no longer be triggered. - * Defaults to "false". + * Defaults to "AUTO". */ - private Boolean constraintStreamAutomaticNodeSharing; + private AutomaticNodeSharing constraintStreamAutomaticNodeSharing; /** * Configuration of the random seed. @@ -128,11 +129,11 @@ public void setConstraintStreamProfilingEnabled(Boolean constraintStreamProfilin this.constraintStreamProfilingEnabled = constraintStreamProfilingEnabled; } - public Boolean getConstraintStreamAutomaticNodeSharing() { + public AutomaticNodeSharing getConstraintStreamAutomaticNodeSharing() { return constraintStreamAutomaticNodeSharing; } - public void setConstraintStreamAutomaticNodeSharing(Boolean constraintStreamAutomaticNodeSharing) { + public void setConstraintStreamAutomaticNodeSharing(AutomaticNodeSharing constraintStreamAutomaticNodeSharing) { this.constraintStreamAutomaticNodeSharing = constraintStreamAutomaticNodeSharing; } diff --git a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperty.java b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperty.java index a5d6b4b53e6..53d3b56c8d1 100644 --- a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperty.java +++ b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperty.java @@ -10,6 +10,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import ai.timefold.solver.core.config.score.director.AutomaticNodeSharing; import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.config.solver.PreviewFeature; import ai.timefold.solver.core.impl.heuristic.selector.common.nearby.NearbyDistanceMeter; @@ -44,7 +45,7 @@ public enum SolverProperty { SolverProperties::setConstraintStreamProfilingEnabled, value -> Boolean.valueOf(value.toString())), CONSTRAINT_STREAM_AUTOMATIC_NODE_SHARING("constraint-stream-automatic-node-sharing", - SolverProperties::setConstraintStreamAutomaticNodeSharing, value -> Boolean.valueOf(value.toString())), + SolverProperties::setConstraintStreamAutomaticNodeSharing, value -> AutomaticNodeSharing.valueOf(value.toString())), RANDOM_SEED("random-seed", SolverProperties::setRandomSeed, value -> Long.parseLong(value.toString())), TERMINATION("termination", SolverProperties::setTermination, value -> { if (value instanceof TerminationProperties terminationProperties) { diff --git a/spring-integration/spring-boot-autoconfigure/src/test/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverConstraintAutoConfigurationTest.java b/spring-integration/spring-boot-autoconfigure/src/test/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverConstraintAutoConfigurationTest.java index 0c84fb01e38..a203f1edec7 100644 --- a/spring-integration/spring-boot-autoconfigure/src/test/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverConstraintAutoConfigurationTest.java +++ b/spring-integration/spring-boot-autoconfigure/src/test/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverConstraintAutoConfigurationTest.java @@ -53,13 +53,13 @@ public TimefoldSolverConstraintAutoConfigurationTest() { .withConfiguration( AutoConfigurations.of(TimefoldSolverAutoConfiguration.class, TimefoldSolverBeanFactory.class)) .withUserConfiguration(NormalSpringTestConfiguration.class) - .withPropertyValues("timefold.solver.%s=true" + .withPropertyValues("timefold.solver.%s=ON" .formatted(SolverProperty.CONSTRAINT_STREAM_AUTOMATIC_NODE_SHARING.getPropertyName())); fakeNativeWithoutNodeSharingContextRunner = new ApplicationContextRunner() .withConfiguration( AutoConfigurations.of(TimefoldSolverAutoConfiguration.class, TimefoldSolverBeanFactory.class)) .withUserConfiguration(NormalSpringTestConfiguration.class) - .withPropertyValues("timefold.solver.%s=false" + .withPropertyValues("timefold.solver.%s=OFF" .formatted(SolverProperty.CONSTRAINT_STREAM_AUTOMATIC_NODE_SHARING.getPropertyName())); testFilteredClassLoader = new FilteredClassLoader(new ClassPathResource(TimefoldProperties.DEFAULT_SOLVER_CONFIG_URL)); diff --git a/tools/benchmark/src/main/resources/benchmark.xsd b/tools/benchmark/src/main/resources/benchmark.xsd index 0f9e9f1f713..cffbf0c34c6 100644 --- a/tools/benchmark/src/main/resources/benchmark.xsd +++ b/tools/benchmark/src/main/resources/benchmark.xsd @@ -446,7 +446,7 @@ - + @@ -2570,6 +2570,27 @@ + + + + + + + + + + + + + + + + + + + + + From 550f14c3d66d9585cba18f79524cbc5c210814f7 Mon Sep 17 00:00:00 2001 From: Christopher Chianelli Date: Wed, 15 Apr 2026 10:49:47 -0400 Subject: [PATCH 2/5] fix: add AutomaticNodeSharing to Spring's native reflect config --- .../reflect-config.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spring-integration/spring-boot-autoconfigure/src/main/resources/META-INF/native-image/ai.timefold.solver/timefold-solver-spring-boot-autoconfigure/reflect-config.json b/spring-integration/spring-boot-autoconfigure/src/main/resources/META-INF/native-image/ai.timefold.solver/timefold-solver-spring-boot-autoconfigure/reflect-config.json index 9d7eda704c9..85ef2c24b64 100644 --- a/spring-integration/spring-boot-autoconfigure/src/main/resources/META-INF/native-image/ai.timefold.solver/timefold-solver-spring-boot-autoconfigure/reflect-config.json +++ b/spring-integration/spring-boot-autoconfigure/src/main/resources/META-INF/native-image/ai.timefold.solver/timefold-solver-spring-boot-autoconfigure/reflect-config.json @@ -575,6 +575,10 @@ } ] }, + { + "name": "ai.timefold.solver.core.config.score.director.AutomaticNodeSharing", + "allDeclaredFields": true + }, { "name": "ai.timefold.solver.core.config.solver.PreviewFeature", "allDeclaredFields": true From 0e64bdaf3559ab0c7bd2375ff6877c11121e1f93 Mon Sep 17 00:00:00 2001 From: Christopher Chianelli Date: Wed, 15 Apr 2026 11:19:18 -0400 Subject: [PATCH 3/5] chore: review comments --- ...g.java => EnableAutomaticNodeSharing.java} | 2 +- .../director/ScoreDirectorFactoryConfig.java | 10 +++--- ...tConstraintStreamScoreDirectorFactory.java | 4 +-- core/src/main/resources/solver.xsd | 4 +-- .../config/SolverBuildTimeConfig.java | 4 +-- ...efoldProcessorNodeSharingFailFastTest.java | 4 +-- .../TimefoldSolverAutoConfiguration.java | 31 ++++++++++++++----- .../config/SolverProperties.java | 8 ++--- .../autoconfigure/config/SolverProperty.java | 5 +-- .../reflect-config.json | 2 +- .../src/main/resources/benchmark.xsd | 4 +-- 11 files changed, 49 insertions(+), 29 deletions(-) rename core/src/main/java/ai/timefold/solver/core/config/score/director/{AutomaticNodeSharing.java => EnableAutomaticNodeSharing.java} (67%) diff --git a/core/src/main/java/ai/timefold/solver/core/config/score/director/AutomaticNodeSharing.java b/core/src/main/java/ai/timefold/solver/core/config/score/director/EnableAutomaticNodeSharing.java similarity index 67% rename from core/src/main/java/ai/timefold/solver/core/config/score/director/AutomaticNodeSharing.java rename to core/src/main/java/ai/timefold/solver/core/config/score/director/EnableAutomaticNodeSharing.java index f44f681a69d..d6df6946e7c 100644 --- a/core/src/main/java/ai/timefold/solver/core/config/score/director/AutomaticNodeSharing.java +++ b/core/src/main/java/ai/timefold/solver/core/config/score/director/EnableAutomaticNodeSharing.java @@ -1,6 +1,6 @@ package ai.timefold.solver.core.config.score.director; -public enum AutomaticNodeSharing { +public enum EnableAutomaticNodeSharing { ON, OFF, AUTO; diff --git a/core/src/main/java/ai/timefold/solver/core/config/score/director/ScoreDirectorFactoryConfig.java b/core/src/main/java/ai/timefold/solver/core/config/score/director/ScoreDirectorFactoryConfig.java index 78c6202cbc8..eeffffe6df6 100644 --- a/core/src/main/java/ai/timefold/solver/core/config/score/director/ScoreDirectorFactoryConfig.java +++ b/core/src/main/java/ai/timefold/solver/core/config/score/director/ScoreDirectorFactoryConfig.java @@ -40,7 +40,8 @@ public final class ScoreDirectorFactoryConfig extends AbstractConfig constraintProviderCustomProperties = null; - private AutomaticNodeSharing constraintStreamAutomaticNodeSharing; + @Nullable + private EnableAutomaticNodeSharing constraintStreamAutomaticNodeSharing; private Boolean constraintStreamProfilingEnabled; private Class incrementalScoreCalculatorClass = null; @@ -92,11 +93,12 @@ public void setConstraintProviderCustomProperties( this.constraintProviderCustomProperties = constraintProviderCustomProperties; } - public @Nullable AutomaticNodeSharing getConstraintStreamAutomaticNodeSharing() { + public @Nullable EnableAutomaticNodeSharing getConstraintStreamAutomaticNodeSharing() { return constraintStreamAutomaticNodeSharing; } - public void setConstraintStreamAutomaticNodeSharing(@Nullable AutomaticNodeSharing constraintStreamAutomaticNodeSharing) { + public void + setConstraintStreamAutomaticNodeSharing(@Nullable EnableAutomaticNodeSharing constraintStreamAutomaticNodeSharing) { this.constraintStreamAutomaticNodeSharing = constraintStreamAutomaticNodeSharing; } @@ -193,7 +195,7 @@ public void setAssertionScoreDirectorFactory(@Nullable ScoreDirectorFactoryConfi } public @NonNull ScoreDirectorFactoryConfig - withConstraintStreamAutomaticNodeSharing(@NonNull AutomaticNodeSharing constraintStreamAutomaticNodeSharing) { + withConstraintStreamAutomaticNodeSharing(@NonNull EnableAutomaticNodeSharing constraintStreamAutomaticNodeSharing) { this.constraintStreamAutomaticNodeSharing = constraintStreamAutomaticNodeSharing; return this; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java index a8c896a6a5d..bca37893ddf 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java @@ -7,7 +7,7 @@ import ai.timefold.solver.core.api.score.Score; import ai.timefold.solver.core.api.score.stream.ConstraintMetaModel; import ai.timefold.solver.core.api.score.stream.ConstraintProvider; -import ai.timefold.solver.core.config.score.director.AutomaticNodeSharing; +import ai.timefold.solver.core.config.score.director.EnableAutomaticNodeSharing; import ai.timefold.solver.core.config.score.director.ScoreDirectorFactoryConfig; import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.config.util.ConfigUtils; @@ -49,7 +49,7 @@ public final class BavetConstraintStreamScoreDirectorFactory getConstraintProviderClass(ScoreDirectorFactoryConfig config, Class providedConstraintProviderClass) { var automaticNodeSharing = Objects.requireNonNullElse(config.getConstraintStreamAutomaticNodeSharing(), - AutomaticNodeSharing.AUTO); + EnableAutomaticNodeSharing.AUTO); return switch (automaticNodeSharing) { case OFF -> providedConstraintProviderClass; case ON -> { diff --git a/core/src/main/resources/solver.xsd b/core/src/main/resources/solver.xsd index 8562561fc0e..235cc6e5467 100644 --- a/core/src/main/resources/solver.xsd +++ b/core/src/main/resources/solver.xsd @@ -103,7 +103,7 @@ - + @@ -1539,7 +1539,7 @@ - + diff --git a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java index 2068cd7cfc7..e8aa6f9e6fa 100644 --- a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java +++ b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java @@ -4,7 +4,7 @@ import java.util.Set; import ai.timefold.solver.core.api.score.stream.ConstraintProvider; -import ai.timefold.solver.core.config.score.director.AutomaticNodeSharing; +import ai.timefold.solver.core.config.score.director.EnableAutomaticNodeSharing; import ai.timefold.solver.core.config.solver.PreviewFeature; import ai.timefold.solver.core.config.solver.SolverConfig; import ai.timefold.solver.quarkus.config.SolverRuntimeConfig; @@ -64,5 +64,5 @@ public interface SolverBuildTimeConfig { * Enterprise Edition. */ // Build time - modifies the ConstraintProvider class if set - Optional constraintStreamAutomaticNodeSharing(); + Optional constraintStreamAutomaticNodeSharing(); } diff --git a/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java b/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java index 94c432a2a4b..f7b9cae18a2 100644 --- a/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java +++ b/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java @@ -4,7 +4,7 @@ import jakarta.inject.Inject; -import ai.timefold.solver.core.config.score.director.AutomaticNodeSharing; +import ai.timefold.solver.core.config.score.director.EnableAutomaticNodeSharing; import ai.timefold.solver.core.config.solver.SolverConfig; import ai.timefold.solver.quarkus.testdomain.normal.TestdataQuarkusConstraintProvider; import ai.timefold.solver.quarkus.testdomain.normal.TestdataQuarkusEntity; @@ -33,7 +33,7 @@ public class TimefoldProcessorNodeSharingFailFastTest { @Test void isEnabledInSolverConfig() { - assertEquals(AutomaticNodeSharing.ON, + assertEquals(EnableAutomaticNodeSharing.ON, solverConfig.getScoreDirectorFactoryConfig().getConstraintStreamAutomaticNodeSharing()); } diff --git a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverAutoConfiguration.java b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverAutoConfiguration.java index 047e11a71ae..149b613a0aa 100644 --- a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverAutoConfiguration.java +++ b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverAutoConfiguration.java @@ -31,11 +31,12 @@ import ai.timefold.solver.core.api.solver.SolutionManager; import ai.timefold.solver.core.api.solver.SolverFactory; import ai.timefold.solver.core.api.solver.SolverManager; -import ai.timefold.solver.core.config.score.director.AutomaticNodeSharing; +import ai.timefold.solver.core.config.score.director.EnableAutomaticNodeSharing; import ai.timefold.solver.core.config.score.director.ScoreDirectorFactoryConfig; import ai.timefold.solver.core.config.solver.SolverConfig; import ai.timefold.solver.core.config.solver.termination.DiminishedReturnsTerminationConfig; import ai.timefold.solver.core.config.solver.termination.TerminationConfig; +import ai.timefold.solver.core.enterprise.TimefoldSolverEnterpriseService; import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor; import ai.timefold.solver.core.impl.io.jaxb.SolverConfigIO; import ai.timefold.solver.spring.boot.autoconfigure.config.DiminishedReturnsProperties; @@ -266,27 +267,43 @@ private void applyScoreDirectorFactoryProperties(IncludeAbstractClassesEntitySca Objects.requireNonNull(solverConfig.getScoreDirectorFactoryConfig()) .setConstraintStreamAutomaticNodeSharing(switch (Objects.requireNonNullElse( solverProperties.getConstraintStreamAutomaticNodeSharing(), - AutomaticNodeSharing.AUTO)) { + EnableAutomaticNodeSharing.AUTO)) { case ON -> { if (NativeDetector.inNativeImage()) { throw new UnsupportedOperationException( "Constraint stream automatic node sharing is unsupported in a Spring native image."); } - yield AutomaticNodeSharing.ON; + yield EnableAutomaticNodeSharing.ON; } - case OFF -> AutomaticNodeSharing.OFF; + case OFF -> EnableAutomaticNodeSharing.OFF; case AUTO -> { if (NativeDetector.inNativeImage()) { - yield AutomaticNodeSharing.OFF; + if (TimefoldSolverEnterpriseService.loadOrDefault( + ignored -> true, + () -> false)) { + // We are in enterprise, so log a note + LOG.debug(""" + Constraint stream automatic node sharing was disabled because it \ + is unsupported in a Spring native image."""); + } + yield EnableAutomaticNodeSharing.OFF; } - yield AutomaticNodeSharing.AUTO; + yield EnableAutomaticNodeSharing.AUTO; } }); } else if (NativeDetector.inNativeImage()) { // Explicitly set it to disabled in a native image if unspecified so // the solver does not try to node share when enterprise is used. + if (TimefoldSolverEnterpriseService.loadOrDefault( + ignored -> true, + () -> false)) { + // We are in enterprise, so log a note + LOG.debug(""" + Constraint stream automatic node sharing was disabled because it \ + is unsupported in a Spring native image."""); + } Objects.requireNonNull(solverConfig.getScoreDirectorFactoryConfig()) - .setConstraintStreamAutomaticNodeSharing(AutomaticNodeSharing.OFF); + .setConstraintStreamAutomaticNodeSharing(EnableAutomaticNodeSharing.OFF); } if (solverProperties.getConstraintStreamProfilingEnabled() != null) { Objects.requireNonNull(solverConfig.getScoreDirectorFactoryConfig()) diff --git a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java index 3a3a4a09627..52bcd03b429 100644 --- a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java +++ b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java @@ -5,7 +5,7 @@ import java.util.TreeSet; import ai.timefold.solver.core.api.score.stream.ConstraintProvider; -import ai.timefold.solver.core.config.score.director.AutomaticNodeSharing; +import ai.timefold.solver.core.config.score.director.EnableAutomaticNodeSharing; import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.config.solver.PreviewFeature; import ai.timefold.solver.core.impl.heuristic.selector.common.nearby.NearbyDistanceMeter; @@ -59,7 +59,7 @@ public class SolverProperties { * will no longer be triggered. * Defaults to "AUTO". */ - private AutomaticNodeSharing constraintStreamAutomaticNodeSharing; + private EnableAutomaticNodeSharing constraintStreamAutomaticNodeSharing; /** * Configuration of the random seed. @@ -129,11 +129,11 @@ public void setConstraintStreamProfilingEnabled(Boolean constraintStreamProfilin this.constraintStreamProfilingEnabled = constraintStreamProfilingEnabled; } - public AutomaticNodeSharing getConstraintStreamAutomaticNodeSharing() { + public EnableAutomaticNodeSharing getConstraintStreamAutomaticNodeSharing() { return constraintStreamAutomaticNodeSharing; } - public void setConstraintStreamAutomaticNodeSharing(AutomaticNodeSharing constraintStreamAutomaticNodeSharing) { + public void setConstraintStreamAutomaticNodeSharing(EnableAutomaticNodeSharing constraintStreamAutomaticNodeSharing) { this.constraintStreamAutomaticNodeSharing = constraintStreamAutomaticNodeSharing; } diff --git a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperty.java b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperty.java index 53d3b56c8d1..f9560178a6a 100644 --- a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperty.java +++ b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperty.java @@ -10,7 +10,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import ai.timefold.solver.core.config.score.director.AutomaticNodeSharing; +import ai.timefold.solver.core.config.score.director.EnableAutomaticNodeSharing; import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.config.solver.PreviewFeature; import ai.timefold.solver.core.impl.heuristic.selector.common.nearby.NearbyDistanceMeter; @@ -45,7 +45,8 @@ public enum SolverProperty { SolverProperties::setConstraintStreamProfilingEnabled, value -> Boolean.valueOf(value.toString())), CONSTRAINT_STREAM_AUTOMATIC_NODE_SHARING("constraint-stream-automatic-node-sharing", - SolverProperties::setConstraintStreamAutomaticNodeSharing, value -> AutomaticNodeSharing.valueOf(value.toString())), + SolverProperties::setConstraintStreamAutomaticNodeSharing, + value -> EnableAutomaticNodeSharing.valueOf(value.toString())), RANDOM_SEED("random-seed", SolverProperties::setRandomSeed, value -> Long.parseLong(value.toString())), TERMINATION("termination", SolverProperties::setTermination, value -> { if (value instanceof TerminationProperties terminationProperties) { diff --git a/spring-integration/spring-boot-autoconfigure/src/main/resources/META-INF/native-image/ai.timefold.solver/timefold-solver-spring-boot-autoconfigure/reflect-config.json b/spring-integration/spring-boot-autoconfigure/src/main/resources/META-INF/native-image/ai.timefold.solver/timefold-solver-spring-boot-autoconfigure/reflect-config.json index 85ef2c24b64..d98698e44e4 100644 --- a/spring-integration/spring-boot-autoconfigure/src/main/resources/META-INF/native-image/ai.timefold.solver/timefold-solver-spring-boot-autoconfigure/reflect-config.json +++ b/spring-integration/spring-boot-autoconfigure/src/main/resources/META-INF/native-image/ai.timefold.solver/timefold-solver-spring-boot-autoconfigure/reflect-config.json @@ -576,7 +576,7 @@ ] }, { - "name": "ai.timefold.solver.core.config.score.director.AutomaticNodeSharing", + "name": "ai.timefold.solver.core.config.score.director.EnableAutomaticNodeSharing", "allDeclaredFields": true }, { diff --git a/tools/benchmark/src/main/resources/benchmark.xsd b/tools/benchmark/src/main/resources/benchmark.xsd index cffbf0c34c6..1b8dc8ef40f 100644 --- a/tools/benchmark/src/main/resources/benchmark.xsd +++ b/tools/benchmark/src/main/resources/benchmark.xsd @@ -446,7 +446,7 @@ - + @@ -2570,7 +2570,7 @@ - + From b19742f4d8ada4ad6a17e58fc7a5170eee4394b0 Mon Sep 17 00:00:00 2001 From: Christopher Chianelli Date: Wed, 15 Apr 2026 14:47:13 -0400 Subject: [PATCH 4/5] chore: change it back to a boolean --- .../director/EnableAutomaticNodeSharing.java | 7 --- .../director/ScoreDirectorFactoryConfig.java | 8 ++-- ...tConstraintStreamScoreDirectorFactory.java | 19 +++----- core/src/main/resources/solver.xsd | 16 +------ .../config/SolverBuildTimeConfig.java | 5 +-- ...efoldProcessorNodeSharingFailFastTest.java | 3 +- .../quarkus/solverConfigWithNodeSharing.xml | 2 +- .../TimefoldSolverAutoConfiguration.java | 43 ++++++------------- .../config/SolverProperties.java | 9 ++-- .../autoconfigure/config/SolverProperty.java | 3 +- .../reflect-config.json | 4 -- ...SolverConstraintAutoConfigurationTest.java | 15 ------- .../src/main/resources/benchmark.xsd | 23 +--------- 13 files changed, 36 insertions(+), 121 deletions(-) delete mode 100644 core/src/main/java/ai/timefold/solver/core/config/score/director/EnableAutomaticNodeSharing.java diff --git a/core/src/main/java/ai/timefold/solver/core/config/score/director/EnableAutomaticNodeSharing.java b/core/src/main/java/ai/timefold/solver/core/config/score/director/EnableAutomaticNodeSharing.java deleted file mode 100644 index d6df6946e7c..00000000000 --- a/core/src/main/java/ai/timefold/solver/core/config/score/director/EnableAutomaticNodeSharing.java +++ /dev/null @@ -1,7 +0,0 @@ -package ai.timefold.solver.core.config.score.director; - -public enum EnableAutomaticNodeSharing { - ON, - OFF, - AUTO; -} diff --git a/core/src/main/java/ai/timefold/solver/core/config/score/director/ScoreDirectorFactoryConfig.java b/core/src/main/java/ai/timefold/solver/core/config/score/director/ScoreDirectorFactoryConfig.java index eeffffe6df6..3f07f491dcb 100644 --- a/core/src/main/java/ai/timefold/solver/core/config/score/director/ScoreDirectorFactoryConfig.java +++ b/core/src/main/java/ai/timefold/solver/core/config/score/director/ScoreDirectorFactoryConfig.java @@ -41,7 +41,7 @@ public final class ScoreDirectorFactoryConfig extends AbstractConfig constraintProviderCustomProperties = null; @Nullable - private EnableAutomaticNodeSharing constraintStreamAutomaticNodeSharing; + private Boolean constraintStreamAutomaticNodeSharing; private Boolean constraintStreamProfilingEnabled; private Class incrementalScoreCalculatorClass = null; @@ -93,12 +93,12 @@ public void setConstraintProviderCustomProperties( this.constraintProviderCustomProperties = constraintProviderCustomProperties; } - public @Nullable EnableAutomaticNodeSharing getConstraintStreamAutomaticNodeSharing() { + public @Nullable Boolean getConstraintStreamAutomaticNodeSharing() { return constraintStreamAutomaticNodeSharing; } public void - setConstraintStreamAutomaticNodeSharing(@Nullable EnableAutomaticNodeSharing constraintStreamAutomaticNodeSharing) { + setConstraintStreamAutomaticNodeSharing(@Nullable Boolean constraintStreamAutomaticNodeSharing) { this.constraintStreamAutomaticNodeSharing = constraintStreamAutomaticNodeSharing; } @@ -195,7 +195,7 @@ public void setAssertionScoreDirectorFactory(@Nullable ScoreDirectorFactoryConfi } public @NonNull ScoreDirectorFactoryConfig - withConstraintStreamAutomaticNodeSharing(@NonNull EnableAutomaticNodeSharing constraintStreamAutomaticNodeSharing) { + withConstraintStreamAutomaticNodeSharing(@NonNull Boolean constraintStreamAutomaticNodeSharing) { this.constraintStreamAutomaticNodeSharing = constraintStreamAutomaticNodeSharing; return this; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java index bca37893ddf..bd6239823f4 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/director/stream/BavetConstraintStreamScoreDirectorFactory.java @@ -7,7 +7,6 @@ import ai.timefold.solver.core.api.score.Score; import ai.timefold.solver.core.api.score.stream.ConstraintMetaModel; import ai.timefold.solver.core.api.score.stream.ConstraintProvider; -import ai.timefold.solver.core.config.score.director.EnableAutomaticNodeSharing; import ai.timefold.solver.core.config.score.director.ScoreDirectorFactoryConfig; import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.config.util.ConfigUtils; @@ -49,20 +48,16 @@ public final class BavetConstraintStreamScoreDirectorFactory getConstraintProviderClass(ScoreDirectorFactoryConfig config, Class providedConstraintProviderClass) { var automaticNodeSharing = Objects.requireNonNullElse(config.getConstraintStreamAutomaticNodeSharing(), - EnableAutomaticNodeSharing.AUTO); - return switch (automaticNodeSharing) { - case OFF -> providedConstraintProviderClass; - case ON -> { - var enterpriseService = - TimefoldSolverEnterpriseService - .loadOrFail(TimefoldSolverEnterpriseService.Feature.AUTOMATIC_NODE_SHARING); - yield enterpriseService.createNodeSharer().buildNodeSharedConstraintProvider(providedConstraintProviderClass); - } - case AUTO -> TimefoldSolverEnterpriseService.loadOrDefault( + true); + + if (automaticNodeSharing) { + return TimefoldSolverEnterpriseService.loadOrDefault( enterpriseService -> enterpriseService.createNodeSharer() .buildNodeSharedConstraintProvider(providedConstraintProviderClass), () -> providedConstraintProviderClass); - }; + } else { + return providedConstraintProviderClass; + } } private final BavetConstraintSessionFactory constraintSessionFactory; diff --git a/core/src/main/resources/solver.xsd b/core/src/main/resources/solver.xsd index 235cc6e5467..8e4c489f223 100644 --- a/core/src/main/resources/solver.xsd +++ b/core/src/main/resources/solver.xsd @@ -103,7 +103,7 @@ - + @@ -1539,20 +1539,6 @@ - - - - - - - - - - - - - - diff --git a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java index e8aa6f9e6fa..19751d96c6a 100644 --- a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java +++ b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java @@ -4,7 +4,6 @@ import java.util.Set; import ai.timefold.solver.core.api.score.stream.ConstraintProvider; -import ai.timefold.solver.core.config.score.director.EnableAutomaticNodeSharing; import ai.timefold.solver.core.config.solver.PreviewFeature; import ai.timefold.solver.core.config.solver.SolverConfig; import ai.timefold.solver.quarkus.config.SolverRuntimeConfig; @@ -58,11 +57,11 @@ public interface SolverBuildTimeConfig { * so nodes share lambdas when possible, improving performance. * When enabled, breakpoints placed in the {@link ConstraintProvider} * will no longer be triggered. - * Defaults to "false". + * Defaults to "true". *

* Note: this setting is only available in Timefold Solver * Enterprise Edition. */ // Build time - modifies the ConstraintProvider class if set - Optional constraintStreamAutomaticNodeSharing(); + Optional constraintStreamAutomaticNodeSharing(); } diff --git a/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java b/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java index f7b9cae18a2..c2c20170fc8 100644 --- a/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java +++ b/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java @@ -4,7 +4,6 @@ import jakarta.inject.Inject; -import ai.timefold.solver.core.config.score.director.EnableAutomaticNodeSharing; import ai.timefold.solver.core.config.solver.SolverConfig; import ai.timefold.solver.quarkus.testdomain.normal.TestdataQuarkusConstraintProvider; import ai.timefold.solver.quarkus.testdomain.normal.TestdataQuarkusEntity; @@ -33,7 +32,7 @@ public class TimefoldProcessorNodeSharingFailFastTest { @Test void isEnabledInSolverConfig() { - assertEquals(EnableAutomaticNodeSharing.ON, + assertEquals(true, solverConfig.getScoreDirectorFactoryConfig().getConstraintStreamAutomaticNodeSharing()); } diff --git a/quarkus-integration/quarkus/deployment/src/test/resources/ai/timefold/solver/quarkus/solverConfigWithNodeSharing.xml b/quarkus-integration/quarkus/deployment/src/test/resources/ai/timefold/solver/quarkus/solverConfigWithNodeSharing.xml index 414575bb6d0..254768a46a5 100644 --- a/quarkus-integration/quarkus/deployment/src/test/resources/ai/timefold/solver/quarkus/solverConfigWithNodeSharing.xml +++ b/quarkus-integration/quarkus/deployment/src/test/resources/ai/timefold/solver/quarkus/solverConfigWithNodeSharing.xml @@ -2,6 +2,6 @@ - ON + true diff --git a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverAutoConfiguration.java b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverAutoConfiguration.java index 149b613a0aa..1e425f04035 100644 --- a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverAutoConfiguration.java +++ b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverAutoConfiguration.java @@ -31,7 +31,6 @@ import ai.timefold.solver.core.api.solver.SolutionManager; import ai.timefold.solver.core.api.solver.SolverFactory; import ai.timefold.solver.core.api.solver.SolverManager; -import ai.timefold.solver.core.config.score.director.EnableAutomaticNodeSharing; import ai.timefold.solver.core.config.score.director.ScoreDirectorFactoryConfig; import ai.timefold.solver.core.config.solver.SolverConfig; import ai.timefold.solver.core.config.solver.termination.DiminishedReturnsTerminationConfig; @@ -264,33 +263,19 @@ private void applyScoreDirectorFactoryProperties(IncludeAbstractClassesEntitySca SolverConfig solverConfig, SolverProperties solverProperties) { applyScoreDirectorFactoryProperties(entityScanner, solverConfig); if (solverProperties.getConstraintStreamAutomaticNodeSharing() != null) { - Objects.requireNonNull(solverConfig.getScoreDirectorFactoryConfig()) - .setConstraintStreamAutomaticNodeSharing(switch (Objects.requireNonNullElse( - solverProperties.getConstraintStreamAutomaticNodeSharing(), - EnableAutomaticNodeSharing.AUTO)) { - case ON -> { - if (NativeDetector.inNativeImage()) { - throw new UnsupportedOperationException( - "Constraint stream automatic node sharing is unsupported in a Spring native image."); - } - yield EnableAutomaticNodeSharing.ON; - } - case OFF -> EnableAutomaticNodeSharing.OFF; - case AUTO -> { - if (NativeDetector.inNativeImage()) { - if (TimefoldSolverEnterpriseService.loadOrDefault( - ignored -> true, - () -> false)) { - // We are in enterprise, so log a note - LOG.debug(""" - Constraint stream automatic node sharing was disabled because it \ - is unsupported in a Spring native image."""); - } - yield EnableAutomaticNodeSharing.OFF; - } - yield EnableAutomaticNodeSharing.AUTO; - } - }); + if (NativeDetector.inNativeImage()) { + if (solverProperties.getConstraintStreamAutomaticNodeSharing()) { + // We are in enterprise, so log a note + LOG.debug(""" + Constraint stream automatic node sharing was disabled because it \ + is unsupported in a Spring native image."""); + } + Objects.requireNonNull(solverConfig.getScoreDirectorFactoryConfig()) + .setConstraintStreamAutomaticNodeSharing(false); + } else { + Objects.requireNonNull(solverConfig.getScoreDirectorFactoryConfig()) + .setConstraintStreamAutomaticNodeSharing(solverProperties.getConstraintStreamAutomaticNodeSharing()); + } } else if (NativeDetector.inNativeImage()) { // Explicitly set it to disabled in a native image if unspecified so // the solver does not try to node share when enterprise is used. @@ -303,7 +288,7 @@ private void applyScoreDirectorFactoryProperties(IncludeAbstractClassesEntitySca is unsupported in a Spring native image."""); } Objects.requireNonNull(solverConfig.getScoreDirectorFactoryConfig()) - .setConstraintStreamAutomaticNodeSharing(EnableAutomaticNodeSharing.OFF); + .setConstraintStreamAutomaticNodeSharing(false); } if (solverProperties.getConstraintStreamProfilingEnabled() != null) { Objects.requireNonNull(solverConfig.getScoreDirectorFactoryConfig()) diff --git a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java index 52bcd03b429..2fb59f85ed5 100644 --- a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java +++ b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java @@ -5,7 +5,6 @@ import java.util.TreeSet; import ai.timefold.solver.core.api.score.stream.ConstraintProvider; -import ai.timefold.solver.core.config.score.director.EnableAutomaticNodeSharing; import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.config.solver.PreviewFeature; import ai.timefold.solver.core.impl.heuristic.selector.common.nearby.NearbyDistanceMeter; @@ -57,9 +56,9 @@ public class SolverProperties { * so nodes share lambdas when possible, improving performance. * When enabled, breakpoints placed in the {@link ConstraintProvider} * will no longer be triggered. - * Defaults to "AUTO". + * Defaults to "true". */ - private EnableAutomaticNodeSharing constraintStreamAutomaticNodeSharing; + private Boolean constraintStreamAutomaticNodeSharing; /** * Configuration of the random seed. @@ -129,11 +128,11 @@ public void setConstraintStreamProfilingEnabled(Boolean constraintStreamProfilin this.constraintStreamProfilingEnabled = constraintStreamProfilingEnabled; } - public EnableAutomaticNodeSharing getConstraintStreamAutomaticNodeSharing() { + public Boolean getConstraintStreamAutomaticNodeSharing() { return constraintStreamAutomaticNodeSharing; } - public void setConstraintStreamAutomaticNodeSharing(EnableAutomaticNodeSharing constraintStreamAutomaticNodeSharing) { + public void setConstraintStreamAutomaticNodeSharing(Boolean constraintStreamAutomaticNodeSharing) { this.constraintStreamAutomaticNodeSharing = constraintStreamAutomaticNodeSharing; } diff --git a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperty.java b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperty.java index f9560178a6a..d48ec18191b 100644 --- a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperty.java +++ b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperty.java @@ -10,7 +10,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import ai.timefold.solver.core.config.score.director.EnableAutomaticNodeSharing; import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.config.solver.PreviewFeature; import ai.timefold.solver.core.impl.heuristic.selector.common.nearby.NearbyDistanceMeter; @@ -46,7 +45,7 @@ public enum SolverProperty { value -> Boolean.valueOf(value.toString())), CONSTRAINT_STREAM_AUTOMATIC_NODE_SHARING("constraint-stream-automatic-node-sharing", SolverProperties::setConstraintStreamAutomaticNodeSharing, - value -> EnableAutomaticNodeSharing.valueOf(value.toString())), + value -> Boolean.valueOf(value.toString())), RANDOM_SEED("random-seed", SolverProperties::setRandomSeed, value -> Long.parseLong(value.toString())), TERMINATION("termination", SolverProperties::setTermination, value -> { if (value instanceof TerminationProperties terminationProperties) { diff --git a/spring-integration/spring-boot-autoconfigure/src/main/resources/META-INF/native-image/ai.timefold.solver/timefold-solver-spring-boot-autoconfigure/reflect-config.json b/spring-integration/spring-boot-autoconfigure/src/main/resources/META-INF/native-image/ai.timefold.solver/timefold-solver-spring-boot-autoconfigure/reflect-config.json index d98698e44e4..9d7eda704c9 100644 --- a/spring-integration/spring-boot-autoconfigure/src/main/resources/META-INF/native-image/ai.timefold.solver/timefold-solver-spring-boot-autoconfigure/reflect-config.json +++ b/spring-integration/spring-boot-autoconfigure/src/main/resources/META-INF/native-image/ai.timefold.solver/timefold-solver-spring-boot-autoconfigure/reflect-config.json @@ -575,10 +575,6 @@ } ] }, - { - "name": "ai.timefold.solver.core.config.score.director.EnableAutomaticNodeSharing", - "allDeclaredFields": true - }, { "name": "ai.timefold.solver.core.config.solver.PreviewFeature", "allDeclaredFields": true diff --git a/spring-integration/spring-boot-autoconfigure/src/test/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverConstraintAutoConfigurationTest.java b/spring-integration/spring-boot-autoconfigure/src/test/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverConstraintAutoConfigurationTest.java index a203f1edec7..904922f92ab 100644 --- a/spring-integration/spring-boot-autoconfigure/src/test/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverConstraintAutoConfigurationTest.java +++ b/spring-integration/spring-boot-autoconfigure/src/test/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverConstraintAutoConfigurationTest.java @@ -133,21 +133,6 @@ void readOnlyConcreteProviderClass() { .doesNotThrowAnyException(); } - @Test - void nodeSharingFailFastInNativeImage() { - try (var nativeDetectorMock = Mockito.mockStatic(NativeDetector.class)) { - nativeDetectorMock.when(NativeDetector::inNativeImage).thenReturn(true); - fakeNativeWithNodeSharingContextRunner - .run(context -> { - Throwable startupFailure = context.getStartupFailure(); - assertThat(startupFailure) - .isInstanceOf(UnsupportedOperationException.class) - .hasMessageContainingAll("node sharing", "unsupported", "native"); - }); - } - - } - @Test void nodeSharingDisabledWorksInNativeImage() { try (var nativeDetectorMock = Mockito.mockStatic(NativeDetector.class)) { diff --git a/tools/benchmark/src/main/resources/benchmark.xsd b/tools/benchmark/src/main/resources/benchmark.xsd index 1b8dc8ef40f..0f9e9f1f713 100644 --- a/tools/benchmark/src/main/resources/benchmark.xsd +++ b/tools/benchmark/src/main/resources/benchmark.xsd @@ -446,7 +446,7 @@ - + @@ -2570,27 +2570,6 @@ - - - - - - - - - - - - - - - - - - - - - From 397d11c9301e042023a942c6990322861e431b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Thu, 16 Apr 2026 08:05:38 +0200 Subject: [PATCH 5/5] apply code review --- .../ROOT/pages/constraints-and-score/performance.adoc | 10 +++++----- .../deployment/config/SolverBuildTimeConfig.java | 9 +++++---- ...Test.java => TimefoldProcessorNodeSharingTest.java} | 2 +- .../solver/quarkus/config/SolverRuntimeConfig.java | 2 +- .../boot/autoconfigure/config/SolverProperties.java | 7 ++++--- .../TimefoldSolverConstraintAutoConfigurationTest.java | 9 +-------- 6 files changed, 17 insertions(+), 22 deletions(-) rename quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/{TimefoldProcessorNodeSharingFailFastTest.java => TimefoldProcessorNodeSharingTest.java} (96%) diff --git a/docs/src/modules/ROOT/pages/constraints-and-score/performance.adoc b/docs/src/modules/ROOT/pages/constraints-and-score/performance.adoc index c53feac8300..90485bb6c26 100644 --- a/docs/src/modules/ROOT/pages/constraints-and-score/performance.adoc +++ b/docs/src/modules/ROOT/pages/constraints-and-score/performance.adoc @@ -9,7 +9,6 @@ which is called in its deepest loops. Faster score calculation will return the same solution in less time with the same algorithm, which normally means a better solution in equal time. - [#moveEvaluationSpeed] == Move evaluation and score calculation speed @@ -62,6 +61,11 @@ without forcing you to write a complicated incremental score calculation algorit Note that the speedup is relative to the size of your planning problem (your __n__), making incremental score calculation far more scalable. +[#enterprisePerformanceImprovements] +== Enterprise performance improvements + +The xref:commercial-editions/commercial-editions.adoc[Enterprise Edition] of Timefold Solver has a number of xref:commercial-editions/performance-improvements.adoc[performance improvements] that can further speed up score calculation. +The Enterprise Edition also allows for <>, making it easy to identify constraint performance bottlenecks. [#avoidCallingRemoteServicesDuringScoreCalculation] == Avoid calling remote services during score calculation @@ -305,10 +309,6 @@ The code on the hot path of your application needs to be as fast as possible. * The second best option is to use a `HashMap` or `HashSet`. * Avoid using `java.util.Stream` or any other form of explicit iteration in constraints, as that is much slower. -[#enableAutomaticNodeSharing] -== Enable automatic node sharing -If you are using the xref:commercial-editions/commercial-editions.adoc[Enterprise Edition], you should xref:commercial-editions/performance-improvements.adoc#automaticNodeSharing[enable automatic node sharing] as it can significantly speed up score calculation. - [#benchmark] == Benchmark Whatever you do, benchmark on a large and diverse set of inputs. diff --git a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java index 19751d96c6a..0779466bb16 100644 --- a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java +++ b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/config/SolverBuildTimeConfig.java @@ -31,7 +31,7 @@ public interface SolverBuildTimeConfig { * Enable the Nearby Selection quick configuration. *

* Note: this setting is only available in Timefold Solver - * Enterprise Edition. + * Enterprise Edition. */ // Build time - visited by SolverConfig.visitReferencedClasses // which generates the constructor used by Quarkus @@ -48,7 +48,7 @@ public interface SolverBuildTimeConfig { * If constraint profiling is enabled. Defaults to false. *

* Note: this setting is only available in Timefold Solver - * Enterprise Edition. + * Enterprise Edition. */ Optional constraintStreamProfilingEnabled(); @@ -57,10 +57,11 @@ public interface SolverBuildTimeConfig { * so nodes share lambdas when possible, improving performance. * When enabled, breakpoints placed in the {@link ConstraintProvider} * will no longer be triggered. - * Defaults to "true". + * Defaults to "true"; the solver may decide to disable it regardless, + * if it turns out the feature can not be supported in a given environment. *

* Note: this setting is only available in Timefold Solver - * Enterprise Edition. + * Enterprise Edition. */ // Build time - modifies the ConstraintProvider class if set Optional constraintStreamAutomaticNodeSharing(); diff --git a/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java b/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingTest.java similarity index 96% rename from quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java rename to quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingTest.java index c2c20170fc8..c9e6992ee34 100644 --- a/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingFailFastTest.java +++ b/quarkus-integration/quarkus/deployment/src/test/java/ai/timefold/solver/quarkus/TimefoldProcessorNodeSharingTest.java @@ -16,7 +16,7 @@ import io.quarkus.test.QuarkusUnitTest; -public class TimefoldProcessorNodeSharingFailFastTest { +public class TimefoldProcessorNodeSharingTest { @RegisterExtension static final QuarkusUnitTest config = new QuarkusUnitTest() diff --git a/quarkus-integration/quarkus/runtime/src/main/java/ai/timefold/solver/quarkus/config/SolverRuntimeConfig.java b/quarkus-integration/quarkus/runtime/src/main/java/ai/timefold/solver/quarkus/config/SolverRuntimeConfig.java index 787f13d90bd..a7d9ced6bd5 100644 --- a/quarkus-integration/quarkus/runtime/src/main/java/ai/timefold/solver/quarkus/config/SolverRuntimeConfig.java +++ b/quarkus-integration/quarkus/runtime/src/main/java/ai/timefold/solver/quarkus/config/SolverRuntimeConfig.java @@ -30,7 +30,7 @@ public interface SolverRuntimeConfig { /** * Note: this setting is only available in Timefold Solver - * Enterprise Edition. + * Enterprise Edition. * Enable multithreaded solving for a single problem, which increases CPU consumption. * Defaults to {@value SolverConfig#MOVE_THREAD_COUNT_NONE}. * Other options include {@value SolverConfig#MOVE_THREAD_COUNT_AUTO}, a number diff --git a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java index 2fb59f85ed5..b45127fe40e 100644 --- a/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java +++ b/spring-integration/spring-boot-autoconfigure/src/main/java/ai/timefold/solver/spring/boot/autoconfigure/config/SolverProperties.java @@ -33,7 +33,7 @@ public class SolverProperties { /** * Note: this setting is only available in Timefold Solver - * Enterprise Edition. + * Enterprise Edition. * Enable multithreaded solving for a single problem, which increases CPU consumption. * Defaults to "NONE". * Other options include "AUTO", a number or formula based on the available processor count. @@ -51,12 +51,13 @@ public class SolverProperties { /** * Note: this setting is only available in Timefold Solver - * Enterprise Edition. + * Enterprise Edition. * Enable rewriting the {@link ConstraintProvider} class * so nodes share lambdas when possible, improving performance. * When enabled, breakpoints placed in the {@link ConstraintProvider} * will no longer be triggered. - * Defaults to "true". + * Defaults to "true"; the solver may decide to disable it regardless, + * if it turns out the feature can not be supported in a given environment. */ private Boolean constraintStreamAutomaticNodeSharing; diff --git a/spring-integration/spring-boot-autoconfigure/src/test/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverConstraintAutoConfigurationTest.java b/spring-integration/spring-boot-autoconfigure/src/test/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverConstraintAutoConfigurationTest.java index 904922f92ab..820b3796e93 100644 --- a/spring-integration/spring-boot-autoconfigure/src/test/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverConstraintAutoConfigurationTest.java +++ b/spring-integration/spring-boot-autoconfigure/src/test/java/ai/timefold/solver/spring/boot/autoconfigure/TimefoldSolverConstraintAutoConfigurationTest.java @@ -36,7 +36,6 @@ class TimefoldSolverConstraintAutoConfigurationTest { private final ApplicationContextRunner contextRunner; private final ApplicationContextRunner multiConstraintProviderRunner; - private final ApplicationContextRunner fakeNativeWithNodeSharingContextRunner; private final ApplicationContextRunner fakeNativeWithoutNodeSharingContextRunner; private final FilteredClassLoader testFilteredClassLoader; @@ -49,17 +48,11 @@ public TimefoldSolverConstraintAutoConfigurationTest() { .withConfiguration( AutoConfigurations.of(TimefoldSolverAutoConfiguration.class, TimefoldSolverBeanFactory.class)) .withUserConfiguration(MultipleConstraintSpringTestConfiguration.class); - fakeNativeWithNodeSharingContextRunner = new ApplicationContextRunner() - .withConfiguration( - AutoConfigurations.of(TimefoldSolverAutoConfiguration.class, TimefoldSolverBeanFactory.class)) - .withUserConfiguration(NormalSpringTestConfiguration.class) - .withPropertyValues("timefold.solver.%s=ON" - .formatted(SolverProperty.CONSTRAINT_STREAM_AUTOMATIC_NODE_SHARING.getPropertyName())); fakeNativeWithoutNodeSharingContextRunner = new ApplicationContextRunner() .withConfiguration( AutoConfigurations.of(TimefoldSolverAutoConfiguration.class, TimefoldSolverBeanFactory.class)) .withUserConfiguration(NormalSpringTestConfiguration.class) - .withPropertyValues("timefold.solver.%s=OFF" + .withPropertyValues("timefold.solver.%s=false" .formatted(SolverProperty.CONSTRAINT_STREAM_AUTOMATIC_NODE_SHARING.getPropertyName())); testFilteredClassLoader = new FilteredClassLoader(new ClassPathResource(TimefoldProperties.DEFAULT_SOLVER_CONFIG_URL));