diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 437a210d843..724601e89c1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -91,7 +91,7 @@ CI checks enforce those conventions too, so be sure to build your project with M ./mvnw clean install ``` -For information about how to set up code style checks in your IDE, see [IDE Setup Instructions](https://github.com/TimefoldAI/timefold-solver/blob/main/build/ide-config/ide-configuration.adoc). +For information about how to set up code style checks in your IDE, see [IDE Setup Instructions](build/ide-config/ide-configuration.adoc). **Key code style conventions** (see [Constitution](.specify/memory/constitution.md) for complete details): - Automatic formatting via Maven build diff --git a/build/bom/pom.xml b/build/bom/pom.xml index 53b804cea04..04ef47840a5 100644 --- a/build/bom/pom.xml +++ b/build/bom/pom.xml @@ -48,23 +48,6 @@ test-jar ${version.ai.timefold.solver} - - ai.timefold.solver - timefold-solver-persistence-common - ${version.ai.timefold.solver} - - - ai.timefold.solver - timefold-solver-persistence-common - ${version.ai.timefold.solver} - sources - - - ai.timefold.solver - timefold-solver-persistence-common - test-jar - ${version.ai.timefold.solver} - ai.timefold.solver timefold-solver-jaxb diff --git a/build/build-parent/pom.xml b/build/build-parent/pom.xml index ef9086225db..941f736c740 100644 --- a/build/build-parent/pom.xml +++ b/build/build-parent/pom.xml @@ -26,6 +26,7 @@ 2.25.3 3.27.7 4.3.0 + 3.1.1-RC1 2.3.34 1.5.6 1.0.0 @@ -60,9 +61,6 @@ 3.9.11 21 - - false apply @@ -369,16 +367,6 @@ maven-jar-plugin ${version.jar.plugin} - - default-jar - - - - ${java.module.name} - - - - test-jar @@ -454,6 +442,28 @@ -Xlint:none + + + default-compile + compile + + compile + + + + + + + default-testCompile + test-compile + + testCompile + + + false + + + org.jacoco @@ -568,6 +578,8 @@ maven-surefire-plugin ${version.surefire.plugin} + + false true @@ -575,6 +587,8 @@ maven-failsafe-plugin ${version.surefire.plugin} + + false true diff --git a/build/ide-config/ide-configuration.adoc b/build/ide-config/ide-configuration.adoc index acd0e3aa71f..921e6d1b46b 100644 --- a/build/ide-config/ide-configuration.adoc +++ b/build/ide-config/ide-configuration.adoc @@ -1,11 +1,21 @@ -= IDE code style setup += Configuring your IDE for Timefold Solver + +== Running tests in your IDE + +Timefold Solver is https://en.wikipedia.org/wiki/Java_Platform_Module_System[a modular project]. +However, its test coverage runs on the classpath, not the module path. +Maven respects this and runs the tests on the classpath, but IDEs don't always do that by default. +If you're running your tests in the IDE and seeing errors about missing modules, +you need to configure your IDE to run the tests on the classpath. + +== IDE code style setup Every Maven build formats the source code with the standard code style. This avoids merge conflicts and code style discussions. Configure it in your favorite IDE too: -== IDEA setup +=== IDEA setup . Open the _Settings_ window (or _Preferences_ depending on your edition) and navigate to _Plugins_. @@ -26,7 +36,7 @@ file in the `build/ide-config/src/main/resources/` directory. . Open the _Editor -> Code Style -> Java -> Imports_ settings and set the _Class count to use import with '\*'_ to 999. This is to avoid the `*` imports, which should only be used in special cases. -== Eclipse setup +=== Eclipse setup . Open the _Preferences_ window, and then navigate to _Java -> Code Style -> Formatter_. @@ -36,7 +46,7 @@ file in the `build/ide-config/src/main/resources/` directory. . Click Import and select the `build/ide-config/src/main/resources/eclipse.importorder` file. -== VS Code setup +=== VS Code setup . Open the _Extensions_ window (Ctrl+Shift+X) and search for _Language Support for Java(TM)_ and install it. diff --git a/build/ide-config/pom.xml b/build/ide-config/pom.xml index 14a48aea733..5e52944a076 100644 --- a/build/ide-config/pom.xml +++ b/build/ide-config/pom.xml @@ -20,8 +20,4 @@ https://solver.timefold.ai - - ai.timefold.solver.ide.config - - diff --git a/core/pom.xml b/core/pom.xml index 2241706a131..dbd7122f6a8 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -22,10 +22,6 @@ https://solver.timefold.ai - - ai.timefold.solver.core - - diff --git a/persistence/common/src/main/java/ai/timefold/solver/persistence/common/api/domain/solution/SolutionFileIO.java b/core/src/main/java/ai/timefold/solver/core/api/domain/solution/SolutionFileIO.java similarity index 94% rename from persistence/common/src/main/java/ai/timefold/solver/persistence/common/api/domain/solution/SolutionFileIO.java rename to core/src/main/java/ai/timefold/solver/core/api/domain/solution/SolutionFileIO.java index 0efe23fc346..453e902120f 100644 --- a/persistence/common/src/main/java/ai/timefold/solver/persistence/common/api/domain/solution/SolutionFileIO.java +++ b/core/src/main/java/ai/timefold/solver/core/api/domain/solution/SolutionFileIO.java @@ -1,9 +1,7 @@ -package ai.timefold.solver.persistence.common.api.domain.solution; +package ai.timefold.solver.core.api.domain.solution; import java.io.File; -import ai.timefold.solver.core.api.domain.solution.PlanningSolution; - /** * Reads or writes a {@link PlanningSolution} from or to a {@link File}. *

diff --git a/core/src/main/java/ai/timefold/solver/core/config/heuristic/selector/move/factory/MoveIteratorFactoryConfig.java b/core/src/main/java/ai/timefold/solver/core/config/heuristic/selector/move/factory/MoveIteratorFactoryConfig.java index 77b738d3aee..636cf7ac030 100644 --- a/core/src/main/java/ai/timefold/solver/core/config/heuristic/selector/move/factory/MoveIteratorFactoryConfig.java +++ b/core/src/main/java/ai/timefold/solver/core/config/heuristic/selector/move/factory/MoveIteratorFactoryConfig.java @@ -9,7 +9,7 @@ import ai.timefold.solver.core.config.heuristic.selector.move.MoveSelectorConfig; import ai.timefold.solver.core.config.util.ConfigUtils; import ai.timefold.solver.core.impl.heuristic.selector.move.factory.MoveIteratorFactory; -import ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbCustomPropertiesAdapter; +import ai.timefold.solver.core.impl.io.jaxb.JaxbCustomPropertiesAdapter; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; @@ -25,7 +25,7 @@ public class MoveIteratorFactoryConfig extends MoveSelectorConfig moveIteratorFactoryClass = null; @XmlJavaTypeAdapter(JaxbCustomPropertiesAdapter.class) - protected Map moveIteratorFactoryCustomProperties = null; + protected @Nullable Map moveIteratorFactoryCustomProperties = null; public @Nullable Class getMoveIteratorFactoryClass() { return moveIteratorFactoryClass; diff --git a/core/src/main/java/ai/timefold/solver/core/config/heuristic/selector/move/factory/MoveListFactoryConfig.java b/core/src/main/java/ai/timefold/solver/core/config/heuristic/selector/move/factory/MoveListFactoryConfig.java index 57d6323a735..b8b4b2392de 100644 --- a/core/src/main/java/ai/timefold/solver/core/config/heuristic/selector/move/factory/MoveListFactoryConfig.java +++ b/core/src/main/java/ai/timefold/solver/core/config/heuristic/selector/move/factory/MoveListFactoryConfig.java @@ -9,7 +9,7 @@ import ai.timefold.solver.core.config.heuristic.selector.move.MoveSelectorConfig; import ai.timefold.solver.core.config.util.ConfigUtils; import ai.timefold.solver.core.impl.heuristic.selector.move.factory.MoveListFactory; -import ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbCustomPropertiesAdapter; +import ai.timefold.solver.core.impl.io.jaxb.JaxbCustomPropertiesAdapter; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; diff --git a/core/src/main/java/ai/timefold/solver/core/config/partitionedsearch/PartitionedSearchPhaseConfig.java b/core/src/main/java/ai/timefold/solver/core/config/partitionedsearch/PartitionedSearchPhaseConfig.java index c3cb27d9bb3..746f523e839 100644 --- a/core/src/main/java/ai/timefold/solver/core/config/partitionedsearch/PartitionedSearchPhaseConfig.java +++ b/core/src/main/java/ai/timefold/solver/core/config/partitionedsearch/PartitionedSearchPhaseConfig.java @@ -16,7 +16,7 @@ import ai.timefold.solver.core.config.phase.PhaseConfig; import ai.timefold.solver.core.config.phase.custom.CustomPhaseConfig; import ai.timefold.solver.core.config.util.ConfigUtils; -import ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbCustomPropertiesAdapter; +import ai.timefold.solver.core.impl.io.jaxb.JaxbCustomPropertiesAdapter; import ai.timefold.solver.core.impl.partitionedsearch.partitioner.SolutionPartitioner; import org.jspecify.annotations.NonNull; diff --git a/core/src/main/java/ai/timefold/solver/core/config/phase/custom/CustomPhaseConfig.java b/core/src/main/java/ai/timefold/solver/core/config/phase/custom/CustomPhaseConfig.java index a77f53d7c00..f6604a4c018 100644 --- a/core/src/main/java/ai/timefold/solver/core/config/phase/custom/CustomPhaseConfig.java +++ b/core/src/main/java/ai/timefold/solver/core/config/phase/custom/CustomPhaseConfig.java @@ -15,7 +15,7 @@ import ai.timefold.solver.core.api.solver.phase.PhaseCommand; import ai.timefold.solver.core.config.phase.PhaseConfig; import ai.timefold.solver.core.config.util.ConfigUtils; -import ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbCustomPropertiesAdapter; +import ai.timefold.solver.core.impl.io.jaxb.JaxbCustomPropertiesAdapter; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; 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 1ff051377e9..2eefc09a9d9 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 @@ -12,7 +12,7 @@ import ai.timefold.solver.core.api.score.stream.ConstraintProvider; import ai.timefold.solver.core.config.AbstractConfig; import ai.timefold.solver.core.config.util.ConfigUtils; -import ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbCustomPropertiesAdapter; +import ai.timefold.solver.core.impl.io.jaxb.JaxbCustomPropertiesAdapter; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; diff --git a/core/src/main/java/ai/timefold/solver/core/config/solver/termination/DiminishedReturnsTerminationConfig.java b/core/src/main/java/ai/timefold/solver/core/config/solver/termination/DiminishedReturnsTerminationConfig.java index 06372a64596..a7d6a5dfb87 100644 --- a/core/src/main/java/ai/timefold/solver/core/config/solver/termination/DiminishedReturnsTerminationConfig.java +++ b/core/src/main/java/ai/timefold/solver/core/config/solver/termination/DiminishedReturnsTerminationConfig.java @@ -10,7 +10,7 @@ import ai.timefold.solver.core.config.AbstractConfig; import ai.timefold.solver.core.config.util.ConfigUtils; -import ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbDurationAdapter; +import ai.timefold.solver.core.impl.io.jaxb.JaxbDurationAdapter; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; diff --git a/core/src/main/java/ai/timefold/solver/core/config/solver/termination/TerminationConfig.java b/core/src/main/java/ai/timefold/solver/core/config/solver/termination/TerminationConfig.java index efef5f90cec..64f1418f7a2 100644 --- a/core/src/main/java/ai/timefold/solver/core/config/solver/termination/TerminationConfig.java +++ b/core/src/main/java/ai/timefold/solver/core/config/solver/termination/TerminationConfig.java @@ -11,7 +11,7 @@ import ai.timefold.solver.core.config.AbstractConfig; import ai.timefold.solver.core.config.util.ConfigUtils; -import ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbDurationAdapter; +import ai.timefold.solver.core.impl.io.jaxb.JaxbDurationAdapter; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; diff --git a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/GenericJaxbIO.java b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/GenericJaxbIO.java index 7cc7ac44b08..1be600af3d9 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/GenericJaxbIO.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/GenericJaxbIO.java @@ -43,7 +43,7 @@ import org.xml.sax.SAXNotSupportedException; @NullMarked -public final class GenericJaxbIO implements JaxbIO { +public final class GenericJaxbIO { public static DocumentBuilderFactory createDocumentBuilderFactory() { try { @@ -122,7 +122,6 @@ public GenericJaxbIO(Class rootClass, int indentation) { } } - @Override public T read(Reader reader) { try { return (T) createUnmarshaller().unmarshal(reader); @@ -265,7 +264,6 @@ public void validate(Document document, Schema schema) { } } - @Override public void write(T root, Writer writer) { write(root, writer, null); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbAdaptedMap.java b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbAdaptedMap.java new file mode 100644 index 00000000000..527eb311616 --- /dev/null +++ b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbAdaptedMap.java @@ -0,0 +1,33 @@ +package ai.timefold.solver.core.impl.io.jaxb; + +import java.util.List; + +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlType; + +import ai.timefold.solver.core.config.solver.SolverConfig; + +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +// Required to generate the XSD type in the same namespace. +@NullMarked +@XmlType(namespace = SolverConfig.XML_NAMESPACE) +public final class JaxbAdaptedMap { + + @XmlElement(name = "property", namespace = SolverConfig.XML_NAMESPACE) + private @Nullable List entries; + + public JaxbAdaptedMap() { + // Required by JAXB + } + + public JaxbAdaptedMap(@Nullable List entries) { + this.entries = entries; + } + + public @Nullable List getEntries() { + return entries; + } + +} diff --git a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbAdaptedMapEntry.java b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbAdaptedMapEntry.java new file mode 100644 index 00000000000..dab72eba045 --- /dev/null +++ b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbAdaptedMapEntry.java @@ -0,0 +1,37 @@ +package ai.timefold.solver.core.impl.io.jaxb; + +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlType; + +import ai.timefold.solver.core.config.solver.SolverConfig; + +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +// Required to generate the XSD type in the same namespace. +@NullMarked +@XmlType(namespace = SolverConfig.XML_NAMESPACE) +public final class JaxbAdaptedMapEntry { + + @XmlAttribute + private @Nullable String name; + + @XmlAttribute + private @Nullable String value; + + public JaxbAdaptedMapEntry() { + } + + public JaxbAdaptedMapEntry(@Nullable String name, @Nullable String value) { + this.name = name; + this.value = value; + } + + public @Nullable String getName() { + return name; + } + + public @Nullable String getValue() { + return value; + } +} diff --git a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbCustomPropertiesAdapter.java b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbCustomPropertiesAdapter.java new file mode 100644 index 00000000000..057e83e64b2 --- /dev/null +++ b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbCustomPropertiesAdapter.java @@ -0,0 +1,39 @@ +package ai.timefold.solver.core.impl.io.jaxb; + +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; + +import jakarta.xml.bind.annotation.adapters.XmlAdapter; + +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked +public class JaxbCustomPropertiesAdapter extends XmlAdapter> { + + @Override + public @Nullable Map unmarshal(@Nullable JaxbAdaptedMap jaxbAdaptedMap) { + if (jaxbAdaptedMap == null) { + return null; + } + var entries = jaxbAdaptedMap.getEntries(); + if (entries == null || entries.isEmpty()) { + return Collections.emptyMap(); + } + return entries.stream() + .collect(Collectors.toMap(JaxbAdaptedMapEntry::getName, JaxbAdaptedMapEntry::getValue)); + } + + @Override + public @Nullable JaxbAdaptedMap marshal(@Nullable Map originalMap) { + if (originalMap == null) { + return null; + } + var entries = originalMap.entrySet().stream() + .map(entry -> new JaxbAdaptedMapEntry(entry.getKey(), entry.getValue())) + .toList(); + return new JaxbAdaptedMap(entries); + } + +} diff --git a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbDurationAdapter.java b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbDurationAdapter.java similarity index 59% rename from core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbDurationAdapter.java rename to core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbDurationAdapter.java index 19e44ee2cfc..b8792af874b 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbDurationAdapter.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbDurationAdapter.java @@ -1,14 +1,17 @@ -package ai.timefold.solver.core.impl.io.jaxb.adapter; +package ai.timefold.solver.core.impl.io.jaxb; import java.time.Duration; import jakarta.xml.bind.annotation.adapters.XmlAdapter; -// TODO: Move the code to the jaxb-ri +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked public class JaxbDurationAdapter extends XmlAdapter { @Override - public Duration unmarshal(String durationString) { + public @Nullable Duration unmarshal(@Nullable String durationString) { if (durationString == null) { return null; } @@ -16,7 +19,7 @@ public Duration unmarshal(String durationString) { } @Override - public String marshal(Duration duration) { + public @Nullable String marshal(@Nullable Duration duration) { if (duration == null) { return null; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbIO.java b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbIO.java deleted file mode 100644 index 770c26dc6b4..00000000000 --- a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbIO.java +++ /dev/null @@ -1,11 +0,0 @@ -package ai.timefold.solver.core.impl.io.jaxb; - -import java.io.Reader; -import java.io.Writer; - -public interface JaxbIO { - - T read(Reader reader); - - void write(T root, Writer writer); -} diff --git a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbLocaleAdapter.java b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbLocaleAdapter.java similarity index 60% rename from core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbLocaleAdapter.java rename to core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbLocaleAdapter.java index 14ee2b8a0f8..20c647f257b 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbLocaleAdapter.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbLocaleAdapter.java @@ -1,13 +1,17 @@ -package ai.timefold.solver.core.impl.io.jaxb.adapter; +package ai.timefold.solver.core.impl.io.jaxb; import java.util.Locale; import jakarta.xml.bind.annotation.adapters.XmlAdapter; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked public class JaxbLocaleAdapter extends XmlAdapter { @Override - public Locale unmarshal(String localeString) { + public @Nullable Locale unmarshal(@Nullable String localeString) { if (localeString == null) { return null; } @@ -15,7 +19,7 @@ public Locale unmarshal(String localeString) { } @Override - public String marshal(Locale locale) { + public @Nullable String marshal(@Nullable Locale locale) { if (locale == null) { return null; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbOffsetDateTimeAdapter.java b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbOffsetDateTimeAdapter.java similarity index 79% rename from core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbOffsetDateTimeAdapter.java rename to core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbOffsetDateTimeAdapter.java index b37424154ec..cfd62629637 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbOffsetDateTimeAdapter.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/JaxbOffsetDateTimeAdapter.java @@ -1,4 +1,4 @@ -package ai.timefold.solver.core.impl.io.jaxb.adapter; +package ai.timefold.solver.core.impl.io.jaxb; import java.time.DateTimeException; import java.time.OffsetDateTime; @@ -8,8 +8,12 @@ import jakarta.xml.bind.annotation.adapters.XmlAdapter; -// TODO: Move the code to the jaxb-ri +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked public class JaxbOffsetDateTimeAdapter extends XmlAdapter { + private final DateTimeFormatter formatter; public JaxbOffsetDateTimeAdapter() { @@ -21,7 +25,7 @@ public JaxbOffsetDateTimeAdapter() { } @Override - public OffsetDateTime unmarshal(String offsetDateTimeString) { + public @Nullable OffsetDateTime unmarshal(@Nullable String offsetDateTimeString) { if (offsetDateTimeString == null) { return null; } @@ -34,7 +38,7 @@ public OffsetDateTime unmarshal(String offsetDateTimeString) { } @Override - public String marshal(OffsetDateTime offsetDateTimeObject) { + public @Nullable String marshal(@Nullable OffsetDateTime offsetDateTimeObject) { if (offsetDateTimeObject == null) { return null; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/SolverConfigIO.java b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/SolverConfigIO.java index cb9f8a47048..f99192451e6 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/SolverConfigIO.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/SolverConfigIO.java @@ -5,13 +5,14 @@ import ai.timefold.solver.core.config.solver.SolverConfig; +import org.jspecify.annotations.NullMarked; import org.w3c.dom.Document; -public class SolverConfigIO implements JaxbIO { +@NullMarked +public class SolverConfigIO { private static final String SOLVER_XSD_RESOURCE = "/solver.xsd"; private final GenericJaxbIO genericJaxbIO = new GenericJaxbIO<>(SolverConfig.class); - @Override public SolverConfig read(Reader reader) { Document document = genericJaxbIO.parseXml(reader); String rootElementNamespace = document.getDocumentElement().getNamespaceURI(); @@ -29,8 +30,8 @@ public SolverConfig read(Reader reader) { } } - @Override public void write(SolverConfig solverConfig, Writer writer) { genericJaxbIO.writeWithoutNamespaces(solverConfig, writer); } + } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbCustomPropertiesAdapter.java b/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbCustomPropertiesAdapter.java deleted file mode 100644 index 0b14a37c0fe..00000000000 --- a/core/src/main/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbCustomPropertiesAdapter.java +++ /dev/null @@ -1,78 +0,0 @@ -package ai.timefold.solver.core.impl.io.jaxb.adapter; - -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import jakarta.xml.bind.annotation.XmlAttribute; -import jakarta.xml.bind.annotation.XmlElement; -import jakarta.xml.bind.annotation.XmlType; -import jakarta.xml.bind.annotation.adapters.XmlAdapter; - -import ai.timefold.solver.core.config.solver.SolverConfig; - -public class JaxbCustomPropertiesAdapter extends XmlAdapter> { - - @Override - public Map unmarshal(JaxbAdaptedMap jaxbAdaptedMap) { - if (jaxbAdaptedMap == null) { - return null; - } - return jaxbAdaptedMap.entries.stream() - .collect(Collectors.toMap(JaxbAdaptedMapEntry::getName, JaxbAdaptedMapEntry::getValue)); - } - - @Override - public JaxbAdaptedMap marshal(Map originalMap) { - if (originalMap == null) { - return null; - } - List entries = originalMap.entrySet().stream() - .map(entry -> new JaxbCustomPropertiesAdapter.JaxbAdaptedMapEntry(entry.getKey(), entry.getValue())) - .collect(Collectors.toList()); - return new JaxbAdaptedMap(entries); - } - - // Required to generate the XSD type in the same namespace. - @XmlType(namespace = SolverConfig.XML_NAMESPACE) - static class JaxbAdaptedMap { - - @XmlElement(name = "property", namespace = SolverConfig.XML_NAMESPACE) - private List entries; - - private JaxbAdaptedMap() { - // Required by JAXB - } - - public JaxbAdaptedMap(List entries) { - this.entries = entries; - } - } - - // Required to generate the XSD type in the same namespace. - @XmlType(namespace = SolverConfig.XML_NAMESPACE) - static class JaxbAdaptedMapEntry { - - @XmlAttribute - private String name; - - @XmlAttribute - private String value; - - public JaxbAdaptedMapEntry() { - } - - public JaxbAdaptedMapEntry(String name, String value) { - this.name = name; - this.value = value; - } - - public String getName() { - return name; - } - - public String getValue() { - return value; - } - } -} diff --git a/core/src/main/java/module-info.java b/core/src/main/java/module-info.java new file mode 100644 index 00000000000..8456e1a9159 --- /dev/null +++ b/core/src/main/java/module-info.java @@ -0,0 +1,258 @@ +// Divided into categories, in which the packages are sorted alphabetically. +module ai.timefold.solver.core { + + // Public APIs + exports ai.timefold.solver.core.api.domain.common; + exports ai.timefold.solver.core.api.domain.entity; + exports ai.timefold.solver.core.api.domain.solution; + exports ai.timefold.solver.core.api.domain.solution.cloner; + exports ai.timefold.solver.core.api.domain.valuerange; + exports ai.timefold.solver.core.api.domain.variable; + exports ai.timefold.solver.core.api.function; + exports ai.timefold.solver.core.api.score; + exports ai.timefold.solver.core.api.score.analysis; + exports ai.timefold.solver.core.api.score.constraint; + exports ai.timefold.solver.core.api.score.stream; + exports ai.timefold.solver.core.api.score.stream.common; + exports ai.timefold.solver.core.api.score.stream.uni; + exports ai.timefold.solver.core.api.score.stream.bi; + exports ai.timefold.solver.core.api.score.stream.tri; + exports ai.timefold.solver.core.api.score.stream.quad; + exports ai.timefold.solver.core.api.score.stream.penta; + exports ai.timefold.solver.core.api.score.stream.test; + exports ai.timefold.solver.core.api.score.calculator; + exports ai.timefold.solver.core.api.solver; + exports ai.timefold.solver.core.api.solver.change; + exports ai.timefold.solver.core.api.solver.event; + exports ai.timefold.solver.core.api.solver.phase; + + // Config APIs; need to be open to JAXB for XML config parsing, happens below. + exports ai.timefold.solver.core.config; + exports ai.timefold.solver.core.config.constructionheuristic; + exports ai.timefold.solver.core.config.constructionheuristic.decider.forager; + exports ai.timefold.solver.core.config.constructionheuristic.placer; + exports ai.timefold.solver.core.config.exhaustivesearch; + exports ai.timefold.solver.core.config.heuristic.selector.common; + exports ai.timefold.solver.core.config.heuristic.selector.common.decorator; + exports ai.timefold.solver.core.config.heuristic.selector.common.nearby; + exports ai.timefold.solver.core.config.heuristic.selector.entity; + exports ai.timefold.solver.core.config.heuristic.selector.entity.pillar; + exports ai.timefold.solver.core.config.heuristic.selector.list; + exports ai.timefold.solver.core.config.heuristic.selector.move; + exports ai.timefold.solver.core.config.heuristic.selector.move.composite; + exports ai.timefold.solver.core.config.heuristic.selector.move.factory; + exports ai.timefold.solver.core.config.heuristic.selector.move.generic; + exports ai.timefold.solver.core.config.heuristic.selector.move.generic.list; + exports ai.timefold.solver.core.config.heuristic.selector.value; + exports ai.timefold.solver.core.config.localsearch; + exports ai.timefold.solver.core.config.localsearch.decider.acceptor; + exports ai.timefold.solver.core.config.localsearch.decider.acceptor.stepcountinghillclimbing; + exports ai.timefold.solver.core.config.localsearch.decider.forager; + exports ai.timefold.solver.core.config.partitionedsearch; + exports ai.timefold.solver.core.config.phase; + exports ai.timefold.solver.core.config.phase.custom; + exports ai.timefold.solver.core.config.score.director; + exports ai.timefold.solver.core.config.score.trend; + exports ai.timefold.solver.core.config.solver; + exports ai.timefold.solver.core.config.solver.monitoring; + exports ai.timefold.solver.core.config.solver.random; + exports ai.timefold.solver.core.config.solver.termination; + exports ai.timefold.solver.core.config.util; + exports ai.timefold.solver.core.enterprise; + + // Preview APIs + exports ai.timefold.solver.core.preview.api.domain.metamodel; + exports ai.timefold.solver.core.preview.api.domain.solution.diff; + exports ai.timefold.solver.core.preview.api.move; + exports ai.timefold.solver.core.preview.api.move.builtin; + exports ai.timefold.solver.core.preview.api.move.test; + exports ai.timefold.solver.core.preview.api.neighborhood; + exports ai.timefold.solver.core.preview.api.neighborhood.stream; + exports ai.timefold.solver.core.preview.api.neighborhood.stream.enumerating; + exports ai.timefold.solver.core.preview.api.neighborhood.stream.function; + exports ai.timefold.solver.core.preview.api.neighborhood.stream.joiner; + exports ai.timefold.solver.core.preview.api.neighborhood.stream.sampling; + exports ai.timefold.solver.core.preview.api.neighborhood.test; + + // Exporting move selectors and associated code as semi-public API; + // people are using them for custom moves since 1.x. + exports ai.timefold.solver.core.impl.score.director; + exports ai.timefold.solver.core.impl.heuristic.selector.list; + exports ai.timefold.solver.core.impl.heuristic.selector.move; + exports ai.timefold.solver.core.impl.heuristic.selector.move.factory; + exports ai.timefold.solver.core.impl.heuristic.selector.move.generic; + exports ai.timefold.solver.core.impl.heuristic.selector.move.generic.list; + exports ai.timefold.solver.core.impl.heuristic.selector.move.generic.list.kopt; + exports ai.timefold.solver.core.impl.heuristic.selector.move.generic.list.ruin; + + // explicit exports to other modules + exports ai.timefold.solver.core.impl.constructionheuristic.event to + ai.timefold.solver.jackson, ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.constructionheuristic.scope to + ai.timefold.solver.jackson, ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.domain.solution.cloner.gizmo + to ai.timefold.solver.quarkus.deployment; + exports ai.timefold.solver.core.impl.domain.common.accessor + to ai.timefold.solver.quarkus.deployment, ai.timefold.solver.quarkus; + exports ai.timefold.solver.core.impl.domain.common + to ai.timefold.solver.quarkus.deployment; + exports ai.timefold.solver.core.impl.domain.common.accessor.gizmo + to ai.timefold.solver.quarkus.deployment; + exports ai.timefold.solver.core.impl.domain.entity.descriptor + to ai.timefold.solver.jackson, ai.timefold.solver.jaxb, ai.timefold.solver.benchmark, + ai.timefold.solver.spring.boot.autoconfigure, ai.timefold.solver.quarkus.integration.test, + ai.timefold.solver.quarkus, + ai.timefold.solver.quarkus.jackson, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.domain.solution to ai.timefold.solver.jackson, ai.timefold.solver.quarkus.jackson, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.domain.variable.declarative + to ai.timefold.solver.quarkus.deployment, ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.domain.variable.descriptor + to ai.timefold.solver.jackson, ai.timefold.solver.jaxb, ai.timefold.solver.benchmark, + ai.timefold.solver.quarkus, ai.timefold.solver.quarkus.jackson, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.heuristic.move + to ai.timefold.solver.benchmark, ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.heuristic.selector to ai.timefold.solver.jackson, ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.heuristic.selector.common.decorator to ai.timefold.solver.quarkus.deployment, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.heuristic.selector.common.nearby + to ai.timefold.solver.jackson, + ai.timefold.solver.benchmark, + ai.timefold.solver.benchmark.aggregator, + ai.timefold.solver.spring.boot.autoconfigure, ai.timefold.solver.quarkus.deployment, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.heuristic.selector.entity to ai.timefold.solver.jackson, ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.heuristic.selector.entity.pillar + to ai.timefold.solver.jackson, ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.heuristic.selector.value to ai.timefold.solver.jackson, ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.localsearch.event to ai.timefold.solver.jackson, ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.localsearch.scope + to ai.timefold.solver.enterprise.core, ai.timefold.solver.benchmark; + exports ai.timefold.solver.core.impl.partitionedsearch.partitioner to ai.timefold.solver.quarkus.deployment, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.phase.event to ai.timefold.solver.jackson, ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.phase.scope to ai.timefold.solver.jackson, ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.score to ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.score.constraint to ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.score.definition + to ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.score.director.easy to ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.score.director.incremental to ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.score.stream to ai.timefold.solver.jackson, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.score.stream.common + to ai.timefold.solver.quarkus, ai.timefold.solver.spring.boot.autoconfigure; + exports ai.timefold.solver.core.impl.score.stream.collector + to ai.timefold.solver.jackson, ai.timefold.solver.quarkus.jackson, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.score.stream.test + to ai.timefold.solver.quarkus.deployment; + exports ai.timefold.solver.core.impl.score.trend + to ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.solver + to ai.timefold.solver.jackson, ai.timefold.solver.spring.boot.autoconfigure, ai.timefold.solver.benchmark, + ai.timefold.solver.quarkus, + ai.timefold.solver.quarkus.deployment, ai.timefold.solver.quarkus.integration.test, + ai.timefold.solver.quarkus.jackson, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.solver.monitoring to ai.timefold.solver.benchmark, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.solver.scope to + ai.timefold.solver.jackson, ai.timefold.solver.benchmark, ai.timefold.solver.spring.boot.autoconfigure, + ai.timefold.solver.quarkus.deployment, ai.timefold.solver.quarkus.integration.test, + ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.solver.thread + to ai.timefold.solver.enterprise.core, ai.timefold.solver.benchmark; + exports ai.timefold.solver.core.impl.util + to ai.timefold.solver.jackson, ai.timefold.solver.benchmark, + ai.timefold.solver.quarkus.deployment, ai.timefold.solver.quarkus.jackson, + ai.timefold.solver.enterprise.core; + + // enterprise-specific exports + exports ai.timefold.solver.core.impl.bavet.common to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.constructionheuristic.decider to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.constructionheuristic.decider.forager to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.domain.variable to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.domain.variable.supply to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.domain.variable.listener.support to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.heuristic to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.heuristic.selector.common to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.heuristic.selector.common.iterator to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.heuristic.selector.entity.mimic to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.heuristic.selector.list.mimic to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.heuristic.selector.value.mimic to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.localsearch.decider to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.localsearch.decider.acceptor to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.localsearch.decider.forager to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.move to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.neighborhood to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.partitionedsearch to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.phase to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.solver.recaller to ai.timefold.solver.enterprise.core; + exports ai.timefold.solver.core.impl.solver.event to ai.timefold.solver.enterprise.core; + + // Broad impl usage + exports ai.timefold.solver.core.impl.io.jaxb; + exports ai.timefold.solver.core.impl.domain.solution.descriptor; + exports ai.timefold.solver.core.impl.solver.termination; + + // Open configs to JAXB + opens ai.timefold.solver.core.impl.io.jaxb; + opens ai.timefold.solver.core.config to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.constructionheuristic to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.constructionheuristic.decider.forager to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.constructionheuristic.placer to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.exhaustivesearch to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.heuristic.selector.common to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.heuristic.selector.common.decorator to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.heuristic.selector.common.nearby to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.heuristic.selector.entity to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.heuristic.selector.entity.pillar to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.heuristic.selector.list to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.heuristic.selector.move to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.heuristic.selector.move.composite to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.heuristic.selector.move.factory to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.heuristic.selector.move.generic to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.heuristic.selector.move.generic.list to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.heuristic.selector.value to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.localsearch to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.localsearch.decider.acceptor to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.localsearch.decider.acceptor.stepcountinghillclimbing + to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.localsearch.decider.forager to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.partitionedsearch to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.phase to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.phase.custom to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.score.director to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.score.trend to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.solver to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.solver.monitoring to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.solver.random to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.solver.termination to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.core.config.util to jakarta.xml.bind, org.glassfish.jaxb.runtime; + + requires commons.math3; + requires jakarta.xml.bind; + requires java.xml; + requires micrometer.core; + requires org.jspecify; + requires org.slf4j; + requires io.quarkus.gizmo2; +} \ No newline at end of file diff --git a/core/src/main/resources/solver.xsd b/core/src/main/resources/solver.xsd index 3aff40417d8..59b1d167617 100644 --- a/core/src/main/resources/solver.xsd +++ b/core/src/main/resources/solver.xsd @@ -123,7 +123,7 @@ - + @@ -133,7 +133,7 @@ - + diff --git a/core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbCustomPropertiesAdapterTest.java b/core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/JaxbCustomPropertiesAdapterTest.java similarity index 97% rename from core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbCustomPropertiesAdapterTest.java rename to core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/JaxbCustomPropertiesAdapterTest.java index 69b23b4c26d..4c7670753bb 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbCustomPropertiesAdapterTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/JaxbCustomPropertiesAdapterTest.java @@ -1,4 +1,4 @@ -package ai.timefold.solver.core.impl.io.jaxb.adapter; +package ai.timefold.solver.core.impl.io.jaxb; import static org.assertj.core.api.Assertions.assertThat; diff --git a/core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbDurationAdapterTest.java b/core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/JaxbDurationAdapterTest.java similarity index 93% rename from core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbDurationAdapterTest.java rename to core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/JaxbDurationAdapterTest.java index 1e9e2dd3c6f..cc88eb6c355 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbDurationAdapterTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/JaxbDurationAdapterTest.java @@ -1,4 +1,4 @@ -package ai.timefold.solver.core.impl.io.jaxb.adapter; +package ai.timefold.solver.core.impl.io.jaxb; import static org.assertj.core.api.Assertions.assertThat; diff --git a/core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbOffsetDateTimeAdapterTest.java b/core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/JaxbOffsetDateTimeAdapterTest.java similarity index 94% rename from core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbOffsetDateTimeAdapterTest.java rename to core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/JaxbOffsetDateTimeAdapterTest.java index fce217f1294..0275e6bf193 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/adapter/JaxbOffsetDateTimeAdapterTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/io/jaxb/JaxbOffsetDateTimeAdapterTest.java @@ -1,4 +1,4 @@ -package ai.timefold.solver.core.impl.io.jaxb.adapter; +package ai.timefold.solver.core.impl.io.jaxb; import static org.assertj.core.api.Assertions.assertThat; diff --git a/docs/TODO.md b/docs/TODO.md index 39a72b2d111..d120d2edf6c 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -42,5 +42,6 @@ - [ ] `timefold-solver-test` is no more - [ ] `MockProblemChangeDirector` and `ConstraintVerifier` have moved. - [ ] `MoveTester` and `NeighborhoodTester` have moved. +- [ ] `persistence-common` module is no more, `SolutionFileIO` is now in `core`. Remove this file when done. \ No newline at end of file diff --git a/docs/src/modules/ROOT/pages/using-timefold-solver/benchmarking-and-tweaking.adoc b/docs/src/modules/ROOT/pages/using-timefold-solver/benchmarking-and-tweaking.adoc index f6bf714186f..5dfb44766ac 100644 --- a/docs/src/modules/ROOT/pages/using-timefold-solver/benchmarking-and-tweaking.adoc +++ b/docs/src/modules/ROOT/pages/using-timefold-solver/benchmarking-and-tweaking.adoc @@ -191,7 +191,6 @@ public interface SolutionFileIO { } ---- -The `SolutionFileIO` interface is in the `timefold-persistence-common` jar (which is a dependency of the `timefold-solver-benchmark` jar). There are several ways to serialize a solution. diff --git a/persistence/common/pom.xml b/persistence/common/pom.xml deleted file mode 100644 index b262fe4404d..00000000000 --- a/persistence/common/pom.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - 4.0.0 - - ai.timefold.solver - timefold-solver-persistence-parent - 999-SNAPSHOT - - - timefold-solver-persistence-common - - Timefold Solver persistence common - - Timefold solves planning problems. - This lightweight, embeddable planning engine implements powerful and scalable algorithms - to optimize business resource scheduling and planning. - - This module contains the common persistence integration. - - https://solver.timefold.ai - - - ai.timefold.solver.persistence.common - - - - - - ai.timefold.solver - timefold-solver-core - - - ai.timefold.solver - timefold-solver-core - test-jar - test - - - - - - - maven-dependency-plugin - - - - ai.timefold.solver:timefold-solver-core:jar - - - - - - - diff --git a/persistence/common/src/test/resources/logback-test.xml b/persistence/common/src/test/resources/logback-test.xml deleted file mode 100644 index 53393e6deb7..00000000000 --- a/persistence/common/src/test/resources/logback-test.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - %d{HH:mm:ss.SSS} [%-12.12t] %-5p %m%n - - - - - - - - - - - - diff --git a/persistence/jackson/pom.xml b/persistence/jackson/pom.xml index a7dd3904e24..0032afa7350 100644 --- a/persistence/jackson/pom.xml +++ b/persistence/jackson/pom.xml @@ -21,20 +21,12 @@ https://solver.timefold.ai - - ai.timefold.solver.jackson - - ai.timefold.solver timefold-solver-core - - ai.timefold.solver - timefold-solver-persistence-common - ai.timefold.solver timefold-solver-core diff --git a/persistence/jackson/src/main/java/ai/timefold/solver/jackson/impl/domain/solution/JacksonSolutionFileIO.java b/persistence/jackson/src/main/java/ai/timefold/solver/jackson/impl/domain/solution/JacksonSolutionFileIO.java index 6dbbc53c903..126b1aeff39 100644 --- a/persistence/jackson/src/main/java/ai/timefold/solver/jackson/impl/domain/solution/JacksonSolutionFileIO.java +++ b/persistence/jackson/src/main/java/ai/timefold/solver/jackson/impl/domain/solution/JacksonSolutionFileIO.java @@ -4,7 +4,7 @@ import java.io.InputStream; import ai.timefold.solver.core.api.domain.solution.PlanningSolution; -import ai.timefold.solver.persistence.common.api.domain.solution.SolutionFileIO; +import ai.timefold.solver.core.api.domain.solution.SolutionFileIO; import tools.jackson.core.JacksonException; import tools.jackson.databind.ObjectMapper; diff --git a/persistence/jackson/src/main/java/module-info.java b/persistence/jackson/src/main/java/module-info.java new file mode 100644 index 00000000000..8def882aa4b --- /dev/null +++ b/persistence/jackson/src/main/java/module-info.java @@ -0,0 +1,12 @@ +module ai.timefold.solver.jackson { + exports ai.timefold.solver.jackson.api; + + provides tools.jackson.databind.JacksonModule with + ai.timefold.solver.jackson.api.TimefoldJacksonModule; + + requires ai.timefold.solver.core; + requires org.jspecify; + requires tools.jackson.databind; + + uses tools.jackson.databind.JacksonModule; +} \ No newline at end of file diff --git a/persistence/jaxb/pom.xml b/persistence/jaxb/pom.xml index 268368df565..4a7741aae4b 100644 --- a/persistence/jaxb/pom.xml +++ b/persistence/jaxb/pom.xml @@ -21,10 +21,6 @@ https://solver.timefold.ai - - ai.timefold.solver.jaxb - - @@ -37,10 +33,6 @@ test-jar test - - ai.timefold.solver - timefold-solver-persistence-common - diff --git a/persistence/jaxb/src/main/java/ai/timefold/solver/jaxb/impl/domain/solution/JaxbSolutionFileIO.java b/persistence/jaxb/src/main/java/ai/timefold/solver/jaxb/impl/domain/solution/JaxbSolutionFileIO.java index 756cd3eb06f..e6483260dc3 100644 --- a/persistence/jaxb/src/main/java/ai/timefold/solver/jaxb/impl/domain/solution/JaxbSolutionFileIO.java +++ b/persistence/jaxb/src/main/java/ai/timefold/solver/jaxb/impl/domain/solution/JaxbSolutionFileIO.java @@ -16,7 +16,7 @@ import jakarta.xml.bind.Unmarshaller; import ai.timefold.solver.core.api.domain.solution.PlanningSolution; -import ai.timefold.solver.persistence.common.api.domain.solution.SolutionFileIO; +import ai.timefold.solver.core.api.domain.solution.SolutionFileIO; /** * @param the solution type, the class with the {@link PlanningSolution} annotation diff --git a/persistence/jaxb/src/main/java/module-info.java b/persistence/jaxb/src/main/java/module-info.java new file mode 100644 index 00000000000..7496205975c --- /dev/null +++ b/persistence/jaxb/src/main/java/module-info.java @@ -0,0 +1,7 @@ +module ai.timefold.solver.jaxb { + exports ai.timefold.solver.jaxb.api.score; + + requires ai.timefold.solver.core; + requires jakarta.xml.bind; + +} \ No newline at end of file diff --git a/persistence/jpa/pom.xml b/persistence/jpa/pom.xml index 4851f4256fe..6979344a60f 100644 --- a/persistence/jpa/pom.xml +++ b/persistence/jpa/pom.xml @@ -22,7 +22,6 @@ https://solver.timefold.ai - ai.timefold.solver.jpa false diff --git a/persistence/jpa/src/main/java/module-info.java b/persistence/jpa/src/main/java/module-info.java new file mode 100644 index 00000000000..dace4a9b621 --- /dev/null +++ b/persistence/jpa/src/main/java/module-info.java @@ -0,0 +1,6 @@ +module ai.timefold.solver.jpa { + requires jakarta.persistence; + requires ai.timefold.solver.core; + + exports ai.timefold.solver.jpa.api.score; +} \ No newline at end of file diff --git a/persistence/pom.xml b/persistence/pom.xml index e3be007b77d..d18ba0875b1 100644 --- a/persistence/pom.xml +++ b/persistence/pom.xml @@ -24,7 +24,6 @@ https://solver.timefold.ai - common jaxb jackson jpa diff --git a/quarkus-integration/pom.xml b/quarkus-integration/pom.xml index 91eec00eeec..31194accda2 100644 --- a/quarkus-integration/pom.xml +++ b/quarkus-integration/pom.xml @@ -40,6 +40,7 @@ maven-surefire-plugin + false org.jboss.logmanager.LogManager @@ -48,6 +49,7 @@ maven-failsafe-plugin + false org.jboss.logmanager.LogManager diff --git a/quarkus-integration/quarkus-benchmark/deployment/pom.xml b/quarkus-integration/quarkus-benchmark/deployment/pom.xml index 1312e53f719..5b30e5dd342 100644 --- a/quarkus-integration/quarkus-benchmark/deployment/pom.xml +++ b/quarkus-integration/quarkus-benchmark/deployment/pom.xml @@ -13,10 +13,6 @@ Quarkus deployment module for timefold-solver-quarkus-benchmark. https://solver.timefold.ai - - ai.timefold.solver.quarkus.benchmark.deployment - - io.quarkus @@ -26,10 +22,6 @@ io.quarkus quarkus-arc-deployment - - io.quarkus - quarkus-awt-deployment - ai.timefold.solver timefold-solver-quarkus-deployment diff --git a/quarkus-integration/quarkus-benchmark/deployment/src/main/java/ai/timefold/solver/benchmark/quarkus/deployment/TimefoldBenchmarkProcessor.java b/quarkus-integration/quarkus-benchmark/deployment/src/main/java/ai/timefold/solver/benchmark/quarkus/deployment/TimefoldBenchmarkProcessor.java index 866e1bb61bb..78fa4d8b3e9 100644 --- a/quarkus-integration/quarkus-benchmark/deployment/src/main/java/ai/timefold/solver/benchmark/quarkus/deployment/TimefoldBenchmarkProcessor.java +++ b/quarkus-integration/quarkus-benchmark/deployment/src/main/java/ai/timefold/solver/benchmark/quarkus/deployment/TimefoldBenchmarkProcessor.java @@ -8,7 +8,7 @@ import ai.timefold.solver.benchmark.quarkus.UnavailableTimefoldBenchmarkBeanProvider; import ai.timefold.solver.benchmark.quarkus.config.TimefoldBenchmarkRuntimeConfig; import ai.timefold.solver.core.config.solver.SolverConfig; -import ai.timefold.solver.quarkus.deployment.SolverConfigBuildItem; +import ai.timefold.solver.quarkus.deployment.api.SolverConfigBuildItem; import org.jboss.logging.Logger; diff --git a/quarkus-integration/quarkus-benchmark/deployment/src/main/java/module-info.java b/quarkus-integration/quarkus-benchmark/deployment/src/main/java/module-info.java new file mode 100644 index 00000000000..7bf6d12453c --- /dev/null +++ b/quarkus-integration/quarkus-benchmark/deployment/src/main/java/module-info.java @@ -0,0 +1,18 @@ +module ai.timefold.solver.quarkus.benchmark.deployment { + + // Friendly exports. + exports ai.timefold.solver.benchmark.quarkus.deployment + to ai.timefold.solver.enterprise.quarkus.deployment; + + requires ai.timefold.solver.benchmark; + requires ai.timefold.solver.core; + requires ai.timefold.solver.quarkus.benchmark; + requires io.smallrye.config; + requires org.jboss.logging; + requires quarkus.arc.deployment; + requires quarkus.builder; + requires quarkus.core; + requires quarkus.core.deployment; + requires ai.timefold.solver.quarkus.deployment; + +} \ No newline at end of file diff --git a/quarkus-integration/quarkus-benchmark/integration-test/pom.xml b/quarkus-integration/quarkus-benchmark/integration-test/pom.xml index 95f9149a053..356ebaa872a 100644 --- a/quarkus-integration/quarkus-benchmark/integration-test/pom.xml +++ b/quarkus-integration/quarkus-benchmark/integration-test/pom.xml @@ -15,7 +15,6 @@ https://solver.timefold.ai - ai.timefold.solver.quarkus.benchmark **/* diff --git a/quarkus-integration/quarkus-benchmark/integration-test/src/main/java/module-info.java b/quarkus-integration/quarkus-benchmark/integration-test/src/main/java/module-info.java new file mode 100644 index 00000000000..c69ac3a2895 --- /dev/null +++ b/quarkus-integration/quarkus-benchmark/integration-test/src/main/java/module-info.java @@ -0,0 +1,9 @@ +module ai.timefold.solver.quarkus.benchmark.integration.test { + + requires ai.timefold.solver.core; + requires ai.timefold.solver.benchmark; + requires jakarta.inject; + requires jakarta.ws.rs; + requires org.jspecify; + +} \ No newline at end of file diff --git a/quarkus-integration/quarkus-benchmark/pom.xml b/quarkus-integration/quarkus-benchmark/pom.xml index 50da0f8e744..abacfea5619 100644 --- a/quarkus-integration/quarkus-benchmark/pom.xml +++ b/quarkus-integration/quarkus-benchmark/pom.xml @@ -21,4 +21,22 @@ integration-test + + + + + org.eclipse.microprofile.config + microprofile-config-api + ${version.org.eclipse.microprofile.config} + compile + + + + + + org.eclipse.microprofile.config + microprofile-config-api + + + diff --git a/quarkus-integration/quarkus-benchmark/runtime/pom.xml b/quarkus-integration/quarkus-benchmark/runtime/pom.xml index 6de0a508296..512287d2c1e 100644 --- a/quarkus-integration/quarkus-benchmark/runtime/pom.xml +++ b/quarkus-integration/quarkus-benchmark/runtime/pom.xml @@ -14,10 +14,6 @@ Benchmark an Timefold project to power tweak the solver configuration for speed and scalability. https://solver.timefold.ai - - ai.timefold.solver.quarkus.benchmark - - io.quarkus @@ -32,20 +28,9 @@ ai.timefold.solver timefold-solver-quarkus - - io.quarkus - quarkus-awt - - ai.timefold.solver timefold-solver-benchmark - - - ai.timefold.solver - timefold-solver-core - - diff --git a/quarkus-integration/quarkus-benchmark/runtime/src/main/java/module-info.java b/quarkus-integration/quarkus-benchmark/runtime/src/main/java/module-info.java new file mode 100644 index 00000000000..44f17277ff4 --- /dev/null +++ b/quarkus-integration/quarkus-benchmark/runtime/src/main/java/module-info.java @@ -0,0 +1,16 @@ +module ai.timefold.solver.quarkus.benchmark { + + exports ai.timefold.solver.benchmark.quarkus; + exports ai.timefold.solver.benchmark.quarkus.config; + + requires ai.timefold.solver.benchmark; + requires ai.timefold.solver.core; + requires ai.timefold.solver.quarkus; + requires arc; + requires io.smallrye.config; + requires jakarta.cdi; + requires jakarta.inject; + requires org.eclipse.microprofile.config; + requires quarkus.core; + +} \ No newline at end of file diff --git a/quarkus-integration/quarkus-jackson/deployment/pom.xml b/quarkus-integration/quarkus-jackson/deployment/pom.xml index 3fdb8d8f8e9..35a86130262 100644 --- a/quarkus-integration/quarkus-jackson/deployment/pom.xml +++ b/quarkus-integration/quarkus-jackson/deployment/pom.xml @@ -14,10 +14,6 @@ Quarkus deployment module for timefold-solver-quarkus-jackson. https://solver.timefold.ai - - ai.timefold.solver.quarkus.jackson.deployment - - io.quarkus diff --git a/quarkus-integration/quarkus-jackson/deployment/src/main/java/module-info.java b/quarkus-integration/quarkus-jackson/deployment/src/main/java/module-info.java new file mode 100644 index 00000000000..ed0cae68dd9 --- /dev/null +++ b/quarkus-integration/quarkus-jackson/deployment/src/main/java/module-info.java @@ -0,0 +1,6 @@ +module ai.timefold.solver.quarkus.jackson.deployment { + requires quarkus.core.deployment; + requires quarkus.jackson.spi; + requires ai.timefold.solver.quarkus.jackson; + requires quarkus.core; +} \ No newline at end of file diff --git a/quarkus-integration/quarkus-jackson/integration-test/pom.xml b/quarkus-integration/quarkus-jackson/integration-test/pom.xml index 58e79808289..8aa386bb97b 100644 --- a/quarkus-integration/quarkus-jackson/integration-test/pom.xml +++ b/quarkus-integration/quarkus-jackson/integration-test/pom.xml @@ -15,7 +15,6 @@ https://solver.timefold.ai - ai.timefold.solver.quarkus.jackson.integrationtest **/* diff --git a/quarkus-integration/quarkus-jackson/integration-test/src/main/java/module-info.java b/quarkus-integration/quarkus-jackson/integration-test/src/main/java/module-info.java new file mode 100644 index 00000000000..90b8883a924 --- /dev/null +++ b/quarkus-integration/quarkus-jackson/integration-test/src/main/java/module-info.java @@ -0,0 +1,6 @@ +module ai.timefold.solver.quarkus.jackson.integration.test { + requires ai.timefold.solver.core; + requires jakarta.inject; + requires jakarta.ws.rs; + requires org.jspecify; +} \ No newline at end of file diff --git a/quarkus-integration/quarkus-jackson/runtime/pom.xml b/quarkus-integration/quarkus-jackson/runtime/pom.xml index cce3fe39366..b26078f9fb5 100644 --- a/quarkus-integration/quarkus-jackson/runtime/pom.xml +++ b/quarkus-integration/quarkus-jackson/runtime/pom.xml @@ -14,10 +14,6 @@ Activate Jackson bindings for Timefold's Score classes https://solver.timefold.ai - - ai.timefold.solver.quarkus.jackson - - @@ -30,10 +26,6 @@ test-jar test - - ai.timefold.solver - timefold-solver-persistence-common - io.quarkus diff --git a/quarkus-integration/quarkus-jackson/runtime/src/main/java/ai/timefold/solver/quarkus/jackson/solution/JacksonSolutionFileIO.java b/quarkus-integration/quarkus-jackson/runtime/src/main/java/ai/timefold/solver/quarkus/jackson/solution/JacksonSolutionFileIO.java index 7e208c80d1e..c502b0ed70a 100644 --- a/quarkus-integration/quarkus-jackson/runtime/src/main/java/ai/timefold/solver/quarkus/jackson/solution/JacksonSolutionFileIO.java +++ b/quarkus-integration/quarkus-jackson/runtime/src/main/java/ai/timefold/solver/quarkus/jackson/solution/JacksonSolutionFileIO.java @@ -5,7 +5,7 @@ import java.io.InputStream; import ai.timefold.solver.core.api.domain.solution.PlanningSolution; -import ai.timefold.solver.persistence.common.api.domain.solution.SolutionFileIO; +import ai.timefold.solver.core.api.domain.solution.SolutionFileIO; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; diff --git a/quarkus-integration/quarkus-jackson/runtime/src/main/java/module-info.java b/quarkus-integration/quarkus-jackson/runtime/src/main/java/module-info.java new file mode 100644 index 00000000000..6e3cb6592dd --- /dev/null +++ b/quarkus-integration/quarkus-jackson/runtime/src/main/java/module-info.java @@ -0,0 +1,7 @@ +module ai.timefold.solver.quarkus.jackson { + exports ai.timefold.solver.quarkus.jackson; + + requires ai.timefold.solver.core; + requires com.fasterxml.jackson.databind; + requires org.jspecify; +} \ No newline at end of file diff --git a/quarkus-integration/quarkus/deployment/pom.xml b/quarkus-integration/quarkus/deployment/pom.xml index e77ade56f95..f02a9e7e0c1 100644 --- a/quarkus-integration/quarkus/deployment/pom.xml +++ b/quarkus-integration/quarkus/deployment/pom.xml @@ -14,10 +14,6 @@ Quarkus deployment module for timefold-solver-quarkus. https://solver.timefold.ai - - ai.timefold.solver.quarkus.deployment - - io.quarkus diff --git a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/GeneratedGizmoClasses.java b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/GeneratedGizmoClasses.java deleted file mode 100644 index 71f468c8901..00000000000 --- a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/GeneratedGizmoClasses.java +++ /dev/null @@ -1,14 +0,0 @@ -package ai.timefold.solver.quarkus.deployment; - -import java.util.Set; - -public class GeneratedGizmoClasses { - Set generatedGizmoMemberAccessorClassSet; - Set generatedGizmoSolutionClonerClassSet; - - public GeneratedGizmoClasses(Set generatedGizmoMemberAccessorClassSet, - Set generatedGizmoSolutionClonerClassSet) { - this.generatedGizmoMemberAccessorClassSet = generatedGizmoMemberAccessorClassSet; - this.generatedGizmoSolutionClonerClassSet = generatedGizmoSolutionClonerClassSet; - } -} 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 e2fa1df722d..89756aed1f0 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 @@ -52,6 +52,8 @@ import ai.timefold.solver.quarkus.bean.UnavailableTimefoldBeanProvider; import ai.timefold.solver.quarkus.config.TimefoldRuntimeConfig; import ai.timefold.solver.quarkus.deployment.api.ConstraintMetaModelBuildItem; +import ai.timefold.solver.quarkus.deployment.api.GeneratedGizmoClasses; +import ai.timefold.solver.quarkus.deployment.api.SolverConfigBuildItem; import ai.timefold.solver.quarkus.deployment.config.SolverBuildTimeConfig; import ai.timefold.solver.quarkus.deployment.config.TimefoldBuildTimeConfig; import ai.timefold.solver.quarkus.devui.DevUISolverConfig; @@ -636,10 +638,10 @@ void recordAndRegisterRuntimeBeans(TimefoldRecorder recorder, RecorderContext re .supplier(recorder.solverConfigSupplier(key, value, GizmoMemberAccessorEntityEnhancer.getGeneratedGizmoMemberAccessorMap(recorderContext, solverConfigBuildItem - .getGeneratedGizmoClasses().generatedGizmoMemberAccessorClassSet), + .getGeneratedGizmoClasses().memberAccessorClassSet()), GizmoMemberAccessorEntityEnhancer.getGeneratedSolutionClonerMap(recorderContext, solverConfigBuildItem - .getGeneratedGizmoClasses().generatedGizmoSolutionClonerClassSet))) + .getGeneratedGizmoClasses().solutionClonerClassSet()))) .setRuntimeInit() .defaultBean() .done()); @@ -664,10 +666,10 @@ void recordAndRegisterRuntimeBeans(TimefoldRecorder recorder, RecorderContext re .supplier(recorder.solverManager(key, value, GizmoMemberAccessorEntityEnhancer.getGeneratedGizmoMemberAccessorMap(recorderContext, solverConfigBuildItem - .getGeneratedGizmoClasses().generatedGizmoMemberAccessorClassSet), + .getGeneratedGizmoClasses().memberAccessorClassSet()), GizmoMemberAccessorEntityEnhancer.getGeneratedSolutionClonerMap(recorderContext, solverConfigBuildItem - .getGeneratedGizmoClasses().generatedGizmoSolutionClonerClassSet))) + .getGeneratedGizmoClasses().solutionClonerClassSet()))) .setRuntimeInit() .named(key) .done()); @@ -699,10 +701,10 @@ public void recordAndRegisterDevUIBean( .supplier(devUIRecorder.solverConfigSupplier(solverConfigBuildItem.getSolverConfigMap(), GizmoMemberAccessorEntityEnhancer.getGeneratedGizmoMemberAccessorMap(recorderContext, solverConfigBuildItem - .getGeneratedGizmoClasses().generatedGizmoMemberAccessorClassSet), + .getGeneratedGizmoClasses().memberAccessorClassSet()), GizmoMemberAccessorEntityEnhancer.getGeneratedSolutionClonerMap(recorderContext, solverConfigBuildItem - .getGeneratedGizmoClasses().generatedGizmoSolutionClonerClassSet))) + .getGeneratedGizmoClasses().solutionClonerClassSet()))) .defaultBean() .setRuntimeInit() .done()); diff --git a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/api/ConstraintMetaModelBuildItem.java b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/api/ConstraintMetaModelBuildItem.java index dc3a060db64..9bd36f353ae 100644 --- a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/api/ConstraintMetaModelBuildItem.java +++ b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/api/ConstraintMetaModelBuildItem.java @@ -7,8 +7,7 @@ import io.quarkus.builder.item.SimpleBuildItem; /** - * Represents a {@link ai.timefold.solver.core.api.score.stream.ConstraintMetaModel} at the build time for the purpose - * of Quarkus augmentation. + * Represents a {@link ConstraintMetaModel} at the build time for the purpose of Quarkus augmentation. */ public final class ConstraintMetaModelBuildItem extends SimpleBuildItem { diff --git a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/api/GeneratedGizmoClasses.java b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/api/GeneratedGizmoClasses.java new file mode 100644 index 00000000000..64ab926ad99 --- /dev/null +++ b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/api/GeneratedGizmoClasses.java @@ -0,0 +1,12 @@ +package ai.timefold.solver.quarkus.deployment.api; + +import java.util.Set; + +public record GeneratedGizmoClasses(Set memberAccessorClassSet, Set solutionClonerClassSet) { + + public GeneratedGizmoClasses { + memberAccessorClassSet = Set.copyOf(memberAccessorClassSet); + solutionClonerClassSet = Set.copyOf(solutionClonerClassSet); + } + +} diff --git a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/SolverConfigBuildItem.java b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/api/SolverConfigBuildItem.java similarity index 95% rename from quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/SolverConfigBuildItem.java rename to quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/api/SolverConfigBuildItem.java index 83296258982..388f6199909 100644 --- a/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/SolverConfigBuildItem.java +++ b/quarkus-integration/quarkus/deployment/src/main/java/ai/timefold/solver/quarkus/deployment/api/SolverConfigBuildItem.java @@ -1,4 +1,4 @@ -package ai.timefold.solver.quarkus.deployment; +package ai.timefold.solver.quarkus.deployment.api; import java.util.Map; 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 1984e2fbf2a..a4c4b628494 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 @@ -3,6 +3,7 @@ import java.util.Optional; import java.util.Set; +import ai.timefold.solver.core.api.score.stream.ConstraintProvider; import ai.timefold.solver.core.config.solver.PreviewFeature; import ai.timefold.solver.core.config.solver.SolverConfig; import ai.timefold.solver.quarkus.config.SolverRuntimeConfig; @@ -28,6 +29,10 @@ public interface SolverBuildTimeConfig { /** * Enable the Nearby Selection quick configuration. + *

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

+ * Note: this setting is only available + * for Timefold Solver + * Enterprise Edition. */ Optional constraintStreamProfilingEnabled(); /** - * Note: this setting is only available - * for Timefold Solver - * Enterprise Edition. - * Enable rewriting the {@link ai.timefold.solver.core.api.score.stream.ConstraintProvider} class + * Enable rewriting the {@link ConstraintProvider} class * so nodes share lambdas when possible, improving performance. - * When enabled, breakpoints placed in the {@link ai.timefold.solver.core.api.score.stream.ConstraintProvider} + * When enabled, breakpoints placed in the {@link ConstraintProvider} * will no longer be triggered. * Defaults to "false". + *

+ * Note: this setting is only available + * for Timefold Solver + * Enterprise Edition. */ // Build time - modifies the ConstraintProvider class if set Optional constraintStreamAutomaticNodeSharing(); diff --git a/quarkus-integration/quarkus/deployment/src/main/java/module-info.java b/quarkus-integration/quarkus/deployment/src/main/java/module-info.java new file mode 100644 index 00000000000..3a94ef46bd3 --- /dev/null +++ b/quarkus-integration/quarkus/deployment/src/main/java/module-info.java @@ -0,0 +1,30 @@ +module ai.timefold.solver.quarkus.deployment { + + // Friendly exports. + exports ai.timefold.solver.quarkus.deployment + to ai.timefold.solver.enterprise.quarkus.deployment; + exports ai.timefold.solver.quarkus.deployment.config + to ai.timefold.solver.enterprise.quarkus.deployment; + exports ai.timefold.solver.quarkus.deployment.api + to ai.timefold.solver.enterprise.quarkus.deployment, + ai.timefold.solver.quarkus.benchmark.deployment, + ai.timefold.sdk.quarkus.deployment; + + requires ai.timefold.solver.quarkus; + requires quarkus.arc.deployment; + requires quarkus.builder; + requires quarkus.core.deployment; + requires quarkus.devui.deployment.spi; + requires ai.timefold.solver.core; + requires io.quarkus.gizmo2; + requires jakarta.cdi; + requires quarkus.core; + requires arc.processor; + requires org.jspecify; + requires io.quarkus.gizmo; + requires org.jboss.jandex; + requires io.smallrye.config; + requires org.objectweb.asm; + requires org.jboss.logging; + +} \ No newline at end of file diff --git a/quarkus-integration/quarkus/devui-integration-test/pom.xml b/quarkus-integration/quarkus/devui-integration-test/pom.xml index 84dd9156189..641fc019e83 100644 --- a/quarkus-integration/quarkus/devui-integration-test/pom.xml +++ b/quarkus-integration/quarkus/devui-integration-test/pom.xml @@ -15,7 +15,6 @@ https://solver.timefold.ai - ai.timefold.solver.quarkus **/* diff --git a/quarkus-integration/quarkus/devui-integration-test/src/main/java/module-info.java b/quarkus-integration/quarkus/devui-integration-test/src/main/java/module-info.java new file mode 100644 index 00000000000..49e9c0433a2 --- /dev/null +++ b/quarkus-integration/quarkus/devui-integration-test/src/main/java/module-info.java @@ -0,0 +1,4 @@ +module ai.timefold.solver.quarkus.devui.integration.test { + requires ai.timefold.solver.core; + requires org.jspecify; +} \ No newline at end of file diff --git a/quarkus-integration/quarkus/integration-test/pom.xml b/quarkus-integration/quarkus/integration-test/pom.xml index 8fba7489238..dead8201847 100644 --- a/quarkus-integration/quarkus/integration-test/pom.xml +++ b/quarkus-integration/quarkus/integration-test/pom.xml @@ -15,7 +15,6 @@ https://solver.timefold.ai - ai.timefold.solver.quarkus **/* diff --git a/quarkus-integration/quarkus/integration-test/src/main/java/module-info.java b/quarkus-integration/quarkus/integration-test/src/main/java/module-info.java new file mode 100644 index 00000000000..a123ebdef78 --- /dev/null +++ b/quarkus-integration/quarkus/integration-test/src/main/java/module-info.java @@ -0,0 +1,6 @@ +module ai.timefold.solver.quarkus.integration.test { + requires ai.timefold.solver.core; + requires jakarta.inject; + requires jakarta.ws.rs; + requires org.jspecify; +} \ No newline at end of file diff --git a/quarkus-integration/quarkus/pom.xml b/quarkus-integration/quarkus/pom.xml index 8d18f218942..8a918ba56f7 100644 --- a/quarkus-integration/quarkus/pom.xml +++ b/quarkus-integration/quarkus/pom.xml @@ -22,4 +22,22 @@ reflection-integration-test devui-integration-test + + + + + + org.eclipse.microprofile.config + microprofile-config-api + ${version.org.eclipse.microprofile.config} + compile + + + + + + org.eclipse.microprofile.config + microprofile-config-api + + diff --git a/quarkus-integration/quarkus/reflection-integration-test/pom.xml b/quarkus-integration/quarkus/reflection-integration-test/pom.xml index 2dcf6fc81ba..ba56b300fc3 100644 --- a/quarkus-integration/quarkus/reflection-integration-test/pom.xml +++ b/quarkus-integration/quarkus/reflection-integration-test/pom.xml @@ -15,7 +15,6 @@ https://solver.timefold.ai - ai.timefold.solver.quarkus **/* diff --git a/quarkus-integration/quarkus/reflection-integration-test/src/main/java/module-info.java b/quarkus-integration/quarkus/reflection-integration-test/src/main/java/module-info.java new file mode 100644 index 00000000000..61acd4909bf --- /dev/null +++ b/quarkus-integration/quarkus/reflection-integration-test/src/main/java/module-info.java @@ -0,0 +1,6 @@ +module ai.timefold.solver.quarkus.reflection.integration.test { + requires ai.timefold.solver.core; + requires jakarta.inject; + requires jakarta.ws.rs; + requires org.jspecify; +} \ No newline at end of file diff --git a/quarkus-integration/quarkus/runtime/pom.xml b/quarkus-integration/quarkus/runtime/pom.xml index 845f1a0867d..67764b7c1cc 100644 --- a/quarkus-integration/quarkus/runtime/pom.xml +++ b/quarkus-integration/quarkus/runtime/pom.xml @@ -14,10 +14,6 @@ Solve planning and scheduling with AI constraint optimization of vehicle routes, employee rosters, maintenance, tasks, lessons, conferences, ... https://solver.timefold.ai - - ai.timefold.solver.quarkus - - io.quarkus diff --git a/quarkus-integration/quarkus/runtime/src/main/java/module-info.java b/quarkus-integration/quarkus/runtime/src/main/java/module-info.java new file mode 100644 index 00000000000..16ada9da6fa --- /dev/null +++ b/quarkus-integration/quarkus/runtime/src/main/java/module-info.java @@ -0,0 +1,21 @@ +module ai.timefold.solver.quarkus { + + exports ai.timefold.solver.quarkus.bean; + exports ai.timefold.solver.quarkus; + exports ai.timefold.solver.quarkus.config; + exports ai.timefold.solver.quarkus.devui; + exports ai.timefold.solver.quarkus.gizmo; + + requires ai.timefold.solver.core; + requires arc; + requires io.vertx.core; + requires jakarta.cdi; + requires jakarta.inject; + requires org.graalvm.nativeimage; + requires org.jspecify; + requires quarkus.core; + requires io.smallrye.config; + requires org.jboss.logging; + requires org.eclipse.microprofile.config; + +} \ No newline at end of file diff --git a/spring-integration/spring-boot-autoconfigure/pom.xml b/spring-integration/spring-boot-autoconfigure/pom.xml index 5df558d5a40..a90838c3e3f 100644 --- a/spring-integration/spring-boot-autoconfigure/pom.xml +++ b/spring-integration/spring-boot-autoconfigure/pom.xml @@ -21,10 +21,6 @@ https://solver.timefold.ai - - ai.timefold.solver.spring.boot.autoconfigure - - diff --git a/spring-integration/spring-boot-autoconfigure/src/main/java/module-info.java b/spring-integration/spring-boot-autoconfigure/src/main/java/module-info.java new file mode 100644 index 00000000000..fea246c2072 --- /dev/null +++ b/spring-integration/spring-boot-autoconfigure/src/main/java/module-info.java @@ -0,0 +1,14 @@ +module ai.timefold.solver.spring.boot.autoconfigure { + requires ai.timefold.solver.jackson; + requires org.apache.commons.logging; + requires spring.beans; + requires spring.boot; + requires spring.boot.autoconfigure; + requires spring.boot.persistence; + requires spring.context; + requires spring.core; + requires tools.jackson.databind; + requires ai.timefold.solver.benchmark; + requires ai.timefold.solver.core; + requires org.jspecify; +} \ No newline at end of file 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 7a0ac997a10..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 @@ -9,7 +9,7 @@ ] }, { - "name": "ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbCustomPropertiesAdapter", + "name": "ai.timefold.solver.core.impl.io.jaxb.JaxbCustomPropertiesAdapter", "methods": [ { "name": "", @@ -18,7 +18,9 @@ ] }, { - "name": "ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbDurationAdapter", + "name": "ai.timefold.solver.core.impl.io.jaxb.JaxbAdaptedMap", + "allDeclaredFields": true, + "queryAllDeclaredMethods": true, "methods": [ { "name": "", @@ -27,7 +29,9 @@ ] }, { - "name": "ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbLocaleAdapter", + "name": "ai.timefold.solver.core.impl.io.jaxb.JaxbAdaptedMapEntry", + "allDeclaredFields": true, + "queryAllDeclaredMethods": true, "methods": [ { "name": "", @@ -36,7 +40,7 @@ ] }, { - "name": "ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbOffsetDateTimeAdapter", + "name": "ai.timefold.solver.core.impl.io.jaxb.JaxbDurationAdapter", "methods": [ { "name": "", @@ -45,12 +49,22 @@ ] }, { - "name": "ai.timefold.solver.core.api.domain.common.DomainAccessType", - "allDeclaredFields": true + "name": "ai.timefold.solver.core.impl.io.jaxb.JaxbLocaleAdapter", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] }, { - "name": "ai.timefold.solver.core.api.score.stream.ConstraintStreamImplType", - "allDeclaredFields": true + "name": "ai.timefold.solver.core.impl.io.jaxb.JaxbOffsetDateTimeAdapter", + "methods": [ + { + "name": "", + "parameterTypes": [] + } + ] }, { "name": "ai.timefold.solver.core.config.AbstractConfig", @@ -625,37 +639,6 @@ } ] }, - { - "name": "ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbCustomPropertiesAdapter", - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, - { - "name": "ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbCustomPropertiesAdapter$JaxbAdaptedMap", - "allDeclaredFields": true, - "queryAllDeclaredMethods": true, - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, - { - "name": "ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbCustomPropertiesAdapter$JaxbAdaptedMapEntry", - "allDeclaredFields": true, - "queryAllDeclaredMethods": true, - "methods": [ - { - "name": "", - "parameterTypes": [] - } - ] - }, { "name": "jakarta.xml.bind.Binder" }, diff --git a/spring-integration/spring-boot-integration-test/pom.xml b/spring-integration/spring-boot-integration-test/pom.xml index 0c59f978fc3..2b946fde7c7 100644 --- a/spring-integration/spring-boot-integration-test/pom.xml +++ b/spring-integration/spring-boot-integration-test/pom.xml @@ -15,7 +15,6 @@ Spring Boot integration tests for Timefold - ai.timefold.solver.spring.boot **/* 1.12.1 @@ -110,9 +109,6 @@ test package - - app.native - add-reachability-metadata @@ -126,7 +122,7 @@ -Ob - --no-fallback -H:+ReportExceptionStackTraces + -H:+ReportExceptionStackTraces true diff --git a/spring-integration/spring-boot-integration-test/src/main/java/module-info.java b/spring-integration/spring-boot-integration-test/src/main/java/module-info.java new file mode 100644 index 00000000000..a002a95c561 --- /dev/null +++ b/spring-integration/spring-boot-integration-test/src/main/java/module-info.java @@ -0,0 +1,7 @@ +module ai.timefold.solver.spring.boot.integration.test { + requires ai.timefold.solver.core; + requires org.jspecify; + requires spring.boot; + requires spring.boot.autoconfigure; + requires spring.web; +} \ No newline at end of file diff --git a/spring-integration/spring-boot-starter/pom.xml b/spring-integration/spring-boot-starter/pom.xml index 9ef5bf7e2b5..d2917847a3c 100644 --- a/spring-integration/spring-boot-starter/pom.xml +++ b/spring-integration/spring-boot-starter/pom.xml @@ -21,10 +21,6 @@ https://solver.timefold.ai - - ai.timefold.solver.spring.boot.starter - - diff --git a/spring-integration/spring-boot-starter/src/main/java/module-info.java b/spring-integration/spring-boot-starter/src/main/java/module-info.java new file mode 100644 index 00000000000..705c5b3e34a --- /dev/null +++ b/spring-integration/spring-boot-starter/src/main/java/module-info.java @@ -0,0 +1,5 @@ +module ai.timefold.solver.spring.boot.starter { + + requires ai.timefold.solver.spring.boot.autoconfigure; + +} \ No newline at end of file diff --git a/tools/benchmark-aggregator/pom.xml b/tools/benchmark-aggregator/pom.xml index 8502e34b40e..7844202af70 100644 --- a/tools/benchmark-aggregator/pom.xml +++ b/tools/benchmark-aggregator/pom.xml @@ -24,10 +24,6 @@ https://solver.timefold.ai - - ai.timefold.solver.benchmark.aggregator - - diff --git a/tools/benchmark-aggregator/src/main/java/module-info.java b/tools/benchmark-aggregator/src/main/java/module-info.java new file mode 100644 index 00000000000..b8ffdf58954 --- /dev/null +++ b/tools/benchmark-aggregator/src/main/java/module-info.java @@ -0,0 +1,7 @@ +module ai.timefold.solver.benchmark.aggregator { + + requires ai.timefold.solver.benchmark; + requires java.desktop; + requires org.slf4j; + +} \ No newline at end of file diff --git a/tools/benchmark-aggregator/src/test/java/ai/timefold/solver/benchmark/aggregator/BenchmarkAggregatorTest.java b/tools/benchmark-aggregator/src/test/java/ai/timefold/solver/benchmark/aggregator/BenchmarkAggregatorTest.java index 621a796f529..d73c6394ef2 100644 --- a/tools/benchmark-aggregator/src/test/java/ai/timefold/solver/benchmark/aggregator/BenchmarkAggregatorTest.java +++ b/tools/benchmark-aggregator/src/test/java/ai/timefold/solver/benchmark/aggregator/BenchmarkAggregatorTest.java @@ -63,7 +63,7 @@ static void setupBenchmarks() throws IOException, InterruptedException { - ai.timefold.solver.persistence.common.api.domain.solution.RigidTestdataSolutionFileIO + ai.timefold.solver.benchmark.util.RigidTestdataSolutionFileIO %s diff --git a/tools/benchmark/pom.xml b/tools/benchmark/pom.xml index 12200c2f65b..e72c337cfde 100644 --- a/tools/benchmark/pom.xml +++ b/tools/benchmark/pom.xml @@ -22,20 +22,12 @@ https://solver.timefold.ai - - ai.timefold.solver.benchmark - - ai.timefold.solver timefold-solver-core - - ai.timefold.solver - timefold-solver-persistence-common - ai.timefold.solver timefold-solver-jaxb @@ -46,12 +38,6 @@ test-jar test - - ai.timefold.solver - timefold-solver-persistence-common - test-jar - test - ai.timefold.solver timefold-solver-jackson diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/config/PlannerBenchmarkConfig.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/config/PlannerBenchmarkConfig.java index dd8277250a1..24be1ca01cd 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/config/PlannerBenchmarkConfig.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/config/PlannerBenchmarkConfig.java @@ -9,7 +9,6 @@ import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; -import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; @@ -25,7 +24,7 @@ import ai.timefold.solver.benchmark.api.PlannerBenchmarkFactory; import ai.timefold.solver.benchmark.config.blueprint.SolverBenchmarkBluePrintConfig; import ai.timefold.solver.benchmark.config.report.BenchmarkReportConfig; -import ai.timefold.solver.benchmark.impl.io.PlannerBenchmarkConfigIO; +import ai.timefold.solver.benchmark.impl.io.jaxb.PlannerBenchmarkConfigIO; import ai.timefold.solver.benchmark.impl.report.BenchmarkReport; import ai.timefold.solver.core.config.solver.SolverConfig; import ai.timefold.solver.core.impl.io.jaxb.TimefoldXmlSerializationException; @@ -33,7 +32,6 @@ import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; -import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; @@ -43,21 +41,10 @@ */ @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name = PlannerBenchmarkConfig.XML_ELEMENT_NAME) -@XmlType(propOrder = { - "name", - "benchmarkDirectory", - "threadFactoryClass", - "parallelBenchmarkCount", - "warmUpMillisecondsSpentLimit", - "warmUpSecondsSpentLimit", - "warmUpMinutesSpentLimit", - "warmUpHoursSpentLimit", - "warmUpDaysSpentLimit", - "benchmarkReportConfig", - "inheritedSolverBenchmarkConfig", - "solverBenchmarkBluePrintConfigList", - "solverBenchmarkConfigList" -}) +@XmlType(propOrder = { "name", "benchmarkDirectory", "threadFactoryClass", "parallelBenchmarkCount", + "warmUpMillisecondsSpentLimit", "warmUpSecondsSpentLimit", "warmUpMinutesSpentLimit", "warmUpHoursSpentLimit", + "warmUpDaysSpentLimit", "benchmarkReportConfig", "inheritedSolverBenchmarkConfig", "solverBenchmarkBluePrintConfigList", + "solverBenchmarkConfigList" }) public class PlannerBenchmarkConfig { public static final String SOLVER_NAMESPACE_PREFIX = "solver"; public static final String XML_ELEMENT_NAME = "plannerBenchmark"; @@ -73,9 +60,9 @@ public class PlannerBenchmarkConfig { public static @NonNull PlannerBenchmarkConfig createFromSolverConfig(@NonNull SolverConfig solverConfig, @NonNull File benchmarkDirectory) { - PlannerBenchmarkConfig plannerBenchmarkConfig = new PlannerBenchmarkConfig(); + var plannerBenchmarkConfig = new PlannerBenchmarkConfig(); plannerBenchmarkConfig.setBenchmarkDirectory(benchmarkDirectory); - SolverBenchmarkConfig solverBenchmarkConfig = new SolverBenchmarkConfig(); + var solverBenchmarkConfig = new SolverBenchmarkConfig(); // Defensive copy of solverConfig solverBenchmarkConfig.setSolverConfig(new SolverConfig(solverConfig)); plannerBenchmarkConfig.setInheritedSolverBenchmarkConfig(solverBenchmarkConfig); @@ -107,22 +94,24 @@ public class PlannerBenchmarkConfig { */ public static @NonNull PlannerBenchmarkConfig createFromXmlResource(@NonNull String benchmarkConfigResource, @Nullable ClassLoader classLoader) { - ClassLoader actualClassLoader = classLoader != null ? classLoader : Thread.currentThread().getContextClassLoader(); - try (InputStream in = actualClassLoader.getResourceAsStream(benchmarkConfigResource)) { + var actualClassLoader = classLoader != null ? classLoader : Thread.currentThread().getContextClassLoader(); + try (var in = actualClassLoader.getResourceAsStream(benchmarkConfigResource)) { if (in == null) { - String errorMessage = "The benchmarkConfigResource (" + benchmarkConfigResource - + ") does not exist as a classpath resource in the classLoader (" + actualClassLoader + ")."; + var errorMessage = """ + The benchmarkConfigResource (%s) does not exist as a classpath resource in the classLoader (%s).""" + .formatted(benchmarkConfigResource, actualClassLoader); if (benchmarkConfigResource.startsWith("/")) { - errorMessage += "\nA classpath resource should not start with a slash (/)." - + " A benchmarkConfigResource adheres to ClassLoader.getResource(String)." - + " Maybe remove the leading slash from the benchmarkConfigResource."; + errorMessage += + """ + A classpath resource should not start with a slash (/). A benchmarkConfigResource adheres to ClassLoader.getResource(String). + Maybe remove the leading slash from the benchmarkConfigResource."""; } throw new IllegalArgumentException(errorMessage); } return createFromXmlInputStream(in, classLoader); } catch (TimefoldXmlSerializationException e) { - throw new IllegalArgumentException("Unmarshalling of benchmarkConfigResource (" + benchmarkConfigResource - + ") fails.", e); + throw new IllegalArgumentException( + "Unmarshalling of benchmarkConfigResource (" + benchmarkConfigResource + ") fails.", e); } catch (IOException e) { throw new IllegalArgumentException("Reading the benchmarkConfigResource (" + benchmarkConfigResource + ") fails.", e); @@ -150,11 +139,14 @@ public class PlannerBenchmarkConfig { try (InputStream in = new FileInputStream(benchmarkConfigFile)) { return createFromXmlInputStream(in, classLoader); } catch (TimefoldXmlSerializationException e) { - throw new IllegalArgumentException("Unmarshalling the benchmarkConfigFile (" + benchmarkConfigFile + ") fails.", e); + throw new IllegalArgumentException("Unmarshalling the benchmarkConfigFile (%s) fails." + .formatted(benchmarkConfigFile), e); } catch (FileNotFoundException e) { - throw new IllegalArgumentException("The benchmarkConfigFile (" + benchmarkConfigFile + ") was not found.", e); + throw new IllegalArgumentException("The benchmarkConfigFile (%s) was not found." + .formatted(benchmarkConfigFile), e); } catch (IOException e) { - throw new IllegalArgumentException("Reading the benchmarkConfigFile (" + benchmarkConfigFile + ") fails.", e); + throw new IllegalArgumentException("Reading the benchmarkConfigFile (%s) fails." + .formatted(benchmarkConfigFile), e); } } @@ -174,8 +166,6 @@ public class PlannerBenchmarkConfig { @Nullable ClassLoader classLoader) { try (Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) { return createFromXmlReader(reader, classLoader); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("This vm does not support the charset (" + StandardCharsets.UTF_8 + ").", e); } catch (IOException e) { throw new IllegalArgumentException("Reading fails.", e); } @@ -195,18 +185,8 @@ public class PlannerBenchmarkConfig { */ public static @NonNull PlannerBenchmarkConfig createFromXmlReader(@NonNull Reader reader, @Nullable ClassLoader classLoader) { - PlannerBenchmarkConfigIO xmlIO = new PlannerBenchmarkConfigIO(); - Object benchmarkConfigObject = xmlIO.read(reader); - if (!(benchmarkConfigObject instanceof PlannerBenchmarkConfig)) { - throw new IllegalArgumentException("The " + PlannerBenchmarkConfig.class.getSimpleName() - + "'s XML root element resolves to a different type (" - + (benchmarkConfigObject == null ? null : benchmarkConfigObject.getClass().getSimpleName()) + ")." - + (benchmarkConfigObject instanceof SolverConfig - ? "\nMaybe use " + PlannerBenchmarkFactory.class.getSimpleName() - + ".createFromSolverConfigXmlResource() instead." - : "")); - } - PlannerBenchmarkConfig benchmarkConfig = (PlannerBenchmarkConfig) benchmarkConfigObject; + var xmlIO = new PlannerBenchmarkConfigIO(); + var benchmarkConfig = xmlIO.read(reader); benchmarkConfig.setClassLoader(classLoader); return benchmarkConfig; } @@ -255,21 +235,24 @@ public class PlannerBenchmarkConfig { */ public static @NonNull PlannerBenchmarkConfig createFromFreemarkerXmlResource(@NonNull String templateResource, @Nullable Object model, @Nullable ClassLoader classLoader) { - ClassLoader actualClassLoader = classLoader != null ? classLoader : Thread.currentThread().getContextClassLoader(); - try (InputStream templateIn = actualClassLoader.getResourceAsStream(templateResource)) { + var actualClassLoader = classLoader != null ? classLoader : Thread.currentThread().getContextClassLoader(); + try (var templateIn = actualClassLoader.getResourceAsStream(templateResource)) { if (templateIn == null) { - String errorMessage = "The templateResource (" + templateResource - + ") does not exist as a classpath resource in the classLoader (" + actualClassLoader + ")."; + var errorMessage = "The templateResource (%s) does not exist as a classpath resource in the classLoader (%s)." + .formatted(templateResource, actualClassLoader); if (templateResource.startsWith("/")) { - errorMessage += "\nA classpath resource should not start with a slash (/)." - + " A templateResource adheres to ClassLoader.getResource(String)." - + " Maybe remove the leading slash from the templateResource."; + errorMessage += + """ + + A classpath resource should not start with a slash (/). A templateResource adheres to ClassLoader.getResource(String). + Maybe remove the leading slash from the templateResource."""; } throw new IllegalArgumentException(errorMessage); } return createFromFreemarkerXmlInputStream(templateIn, model, classLoader); } catch (IOException e) { - throw new IllegalArgumentException("Reading the templateResource (" + templateResource + ") fails.", e); + throw new IllegalArgumentException("Reading the templateResource (%s) fails." + .formatted(templateResource), e); } } @@ -310,12 +293,14 @@ public class PlannerBenchmarkConfig { */ public static @NonNull PlannerBenchmarkConfig createFromFreemarkerXmlFile(@NonNull File templateFile, @Nullable Object model, @Nullable ClassLoader classLoader) { - try (FileInputStream templateIn = new FileInputStream(templateFile)) { + try (var templateIn = new FileInputStream(templateFile)) { return createFromFreemarkerXmlInputStream(templateIn, model, classLoader); } catch (FileNotFoundException e) { - throw new IllegalArgumentException("The templateFile (" + templateFile + ") was not found.", e); + throw new IllegalArgumentException("The templateFile (%s) was not found." + .formatted(templateFile), e); } catch (IOException e) { - throw new IllegalArgumentException("Reading the templateFile (" + templateFile + ") fails.", e); + throw new IllegalArgumentException("Reading the templateFile (%s) fails." + .formatted(templateFile), e); } } @@ -359,8 +344,6 @@ public class PlannerBenchmarkConfig { @Nullable Object model, @Nullable ClassLoader classLoader) { try (Reader reader = new InputStreamReader(templateIn, StandardCharsets.UTF_8)) { return createFromFreemarkerXmlReader(reader, model, classLoader); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException("This vm does not support the charset (" + StandardCharsets.UTF_8 + ").", e); } catch (IOException e) { throw new IllegalArgumentException("Reading fails.", e); } @@ -404,21 +387,21 @@ public class PlannerBenchmarkConfig { */ public static @NonNull PlannerBenchmarkConfig createFromFreemarkerXmlReader(@NonNull Reader templateReader, @Nullable Object model, @Nullable ClassLoader classLoader) { - Configuration freemarkerConfiguration = BenchmarkReport.createFreeMarkerConfiguration(); + var freemarkerConfiguration = BenchmarkReport.createFreeMarkerConfiguration(); freemarkerConfiguration.setNumberFormat("computer"); freemarkerConfiguration.setDateFormat("yyyy-mm-dd"); freemarkerConfiguration.setDateTimeFormat("yyyy-mm-dd HH:mm:ss.SSS z"); freemarkerConfiguration.setTimeFormat("HH:mm:ss.SSS"); String xmlContent; - try (StringWriter xmlContentWriter = new StringWriter()) { - Template template = new Template("benchmarkTemplate.ftl", templateReader, freemarkerConfiguration, + try (var xmlContentWriter = new StringWriter()) { + var template = new Template("benchmarkTemplate.ftl", templateReader, freemarkerConfiguration, freemarkerConfiguration.getDefaultEncoding()); template.process(model, xmlContentWriter); xmlContent = xmlContentWriter.toString(); } catch (TemplateException | IOException e) { throw new IllegalArgumentException("Can not process the Freemarker template into xmlContentWriter.", e); } - try (StringReader configReader = new StringReader(xmlContent)) { + try (var configReader = new StringReader(xmlContent)) { return createFromXmlReader(configReader, classLoader); } } @@ -660,8 +643,8 @@ public void setSolverBenchmarkConfigList(@Nullable List<@NonNull SolverBenchmark return this; } - public @NonNull PlannerBenchmarkConfig withSolverBenchmarkBluePrintConfigs( - @NonNull SolverBenchmarkBluePrintConfig... solverBenchmarkBluePrintConfigs) { + public @NonNull PlannerBenchmarkConfig + withSolverBenchmarkBluePrintConfigs(@NonNull SolverBenchmarkBluePrintConfig... solverBenchmarkBluePrintConfigs) { this.setSolverBenchmarkBluePrintConfigList(List.of(solverBenchmarkBluePrintConfigs)); return this; } diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/config/ProblemBenchmarksConfig.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/config/ProblemBenchmarksConfig.java index 32d4dd0fbe7..9490c236c6e 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/config/ProblemBenchmarksConfig.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/config/ProblemBenchmarksConfig.java @@ -11,9 +11,9 @@ import ai.timefold.solver.benchmark.config.statistic.ProblemStatisticType; import ai.timefold.solver.benchmark.config.statistic.SingleStatisticType; +import ai.timefold.solver.core.api.domain.solution.SolutionFileIO; import ai.timefold.solver.core.config.AbstractConfig; import ai.timefold.solver.core.config.util.ConfigUtils; -import ai.timefold.solver.persistence.common.api.domain.solution.SolutionFileIO; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/config/report/BenchmarkReportConfig.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/config/report/BenchmarkReportConfig.java index b5686186343..6e46cdd5592 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/config/report/BenchmarkReportConfig.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/config/report/BenchmarkReportConfig.java @@ -12,7 +12,7 @@ import ai.timefold.solver.benchmark.impl.result.SolverBenchmarkResult; import ai.timefold.solver.core.config.AbstractConfig; import ai.timefold.solver.core.config.util.ConfigUtils; -import ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbLocaleAdapter; +import ai.timefold.solver.core.impl.io.jaxb.JaxbLocaleAdapter; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/ProblemBenchmarksFactory.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/ProblemBenchmarksFactory.java index af71d8004b6..4a3a3286fb7 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/ProblemBenchmarksFactory.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/ProblemBenchmarksFactory.java @@ -18,10 +18,10 @@ import ai.timefold.solver.benchmark.impl.result.SolverBenchmarkResult; import ai.timefold.solver.benchmark.impl.result.SubSingleBenchmarkResult; import ai.timefold.solver.benchmark.impl.statistic.ProblemStatistic; +import ai.timefold.solver.core.api.domain.solution.SolutionFileIO; import ai.timefold.solver.core.config.util.ConfigUtils; import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor; import ai.timefold.solver.core.impl.solver.DefaultSolverFactory; -import ai.timefold.solver.persistence.common.api.domain.solution.SolutionFileIO; public class ProblemBenchmarksFactory { private final ProblemBenchmarksConfig config; diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/io/PlannerBenchmarkConfigIO.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/io/jaxb/PlannerBenchmarkConfigIO.java similarity index 92% rename from tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/io/PlannerBenchmarkConfigIO.java rename to tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/io/jaxb/PlannerBenchmarkConfigIO.java index d082c7dafba..03c1bf064c4 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/io/PlannerBenchmarkConfigIO.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/io/jaxb/PlannerBenchmarkConfigIO.java @@ -1,4 +1,4 @@ -package ai.timefold.solver.benchmark.impl.io; +package ai.timefold.solver.benchmark.impl.io.jaxb; import java.io.Reader; import java.io.Writer; @@ -7,16 +7,16 @@ import ai.timefold.solver.core.config.solver.SolverConfig; import ai.timefold.solver.core.impl.io.jaxb.ElementNamespaceOverride; import ai.timefold.solver.core.impl.io.jaxb.GenericJaxbIO; -import ai.timefold.solver.core.impl.io.jaxb.JaxbIO; +import org.jspecify.annotations.NullMarked; import org.w3c.dom.Document; -public class PlannerBenchmarkConfigIO implements JaxbIO { +@NullMarked +public class PlannerBenchmarkConfigIO { private static final String BENCHMARK_XSD_RESOURCE = "/benchmark.xsd"; private final GenericJaxbIO genericJaxbIO = new GenericJaxbIO<>(PlannerBenchmarkConfig.class); - @Override public PlannerBenchmarkConfig read(Reader reader) { Document document = genericJaxbIO.parseXml(reader); String rootElementNamespace = document.getDocumentElement().getNamespaceURI(); @@ -41,8 +41,8 @@ public PlannerBenchmarkConfig read(Reader reader) { } } - @Override public void write(PlannerBenchmarkConfig plannerBenchmarkConfig, Writer writer) { genericJaxbIO.writeWithoutNamespaces(plannerBenchmarkConfig, writer); } + } diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/loader/FileProblemProvider.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/loader/FileProblemProvider.java index 930d6dbb796..500b657b8bd 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/loader/FileProblemProvider.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/loader/FileProblemProvider.java @@ -6,7 +6,7 @@ import jakarta.xml.bind.annotation.XmlTransient; import ai.timefold.solver.benchmark.impl.result.SubSingleBenchmarkResult; -import ai.timefold.solver.persistence.common.api.domain.solution.SolutionFileIO; +import ai.timefold.solver.core.api.domain.solution.SolutionFileIO; public class FileProblemProvider implements ProblemProvider { diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/package-info.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/package-info.java index c483d799c02..bf509209d72 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/package-info.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/package-info.java @@ -13,5 +13,5 @@ import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapters; import ai.timefold.solver.core.api.score.Score; -import ai.timefold.solver.core.impl.io.jaxb.adapter.JaxbOffsetDateTimeAdapter; +import ai.timefold.solver.core.impl.io.jaxb.JaxbOffsetDateTimeAdapter; import ai.timefold.solver.jaxb.api.score.PolymorphicScoreJaxbAdapter; diff --git a/tools/benchmark/src/main/java/module-info.java b/tools/benchmark/src/main/java/module-info.java new file mode 100644 index 00000000000..9769c2dea4f --- /dev/null +++ b/tools/benchmark/src/main/java/module-info.java @@ -0,0 +1,44 @@ +module ai.timefold.solver.benchmark { + + requires ai.timefold.solver.core; + requires ai.timefold.solver.jaxb; + requires commons.math3; + requires freemarker; + requires jakarta.xml.bind; + requires java.xml; + requires micrometer.core; + requires org.jspecify; + requires org.slf4j; + + // Public API + exports ai.timefold.solver.benchmark.api; + + // Config APIs; need to be open to JAXB for XML config parsing, happens below. + exports ai.timefold.solver.benchmark.config; + exports ai.timefold.solver.benchmark.config.blueprint; + exports ai.timefold.solver.benchmark.config.ranking; + exports ai.timefold.solver.benchmark.config.report; + exports ai.timefold.solver.benchmark.config.statistic; + + // Contains JAXB serialization. + exports ai.timefold.solver.benchmark.impl.result; + + // Needed by aggregator. + exports ai.timefold.solver.benchmark.impl.report + to ai.timefold.solver.benchmark.aggregator; + exports ai.timefold.solver.benchmark.impl.statistic.common + to ai.timefold.solver.benchmark.aggregator; + + // Open JAXB-serialized types to JAXB. + opens ai.timefold.solver.benchmark.config to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.benchmark.config.blueprint to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.benchmark.config.ranking to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.benchmark.config.report to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.benchmark.config.statistic to jakarta.xml.bind, org.glassfish.jaxb.runtime; + opens ai.timefold.solver.benchmark.impl.result to jakarta.xml.bind, org.glassfish.jaxb.runtime; + + // Friendly usage. + exports ai.timefold.solver.benchmark.impl + to ai.timefold.solver.quarkus.benchmark.integration.test; + +} \ No newline at end of file diff --git a/tools/benchmark/src/main/resources/benchmark.xsd b/tools/benchmark/src/main/resources/benchmark.xsd index 79a4242b0a5..78bcf70e21f 100644 --- a/tools/benchmark/src/main/resources/benchmark.xsd +++ b/tools/benchmark/src/main/resources/benchmark.xsd @@ -476,7 +476,7 @@ - + @@ -491,7 +491,7 @@ - + diff --git a/tools/benchmark/src/test/java/ai/timefold/solver/benchmark/config/PlannerBenchmarkConfigTest.java b/tools/benchmark/src/test/java/ai/timefold/solver/benchmark/config/PlannerBenchmarkConfigTest.java index d9c14d3f9b5..32011cf00fc 100644 --- a/tools/benchmark/src/test/java/ai/timefold/solver/benchmark/config/PlannerBenchmarkConfigTest.java +++ b/tools/benchmark/src/test/java/ai/timefold/solver/benchmark/config/PlannerBenchmarkConfigTest.java @@ -11,11 +11,11 @@ import java.io.Writer; import java.nio.charset.StandardCharsets; -import ai.timefold.solver.benchmark.impl.io.PlannerBenchmarkConfigIO; +import ai.timefold.solver.benchmark.impl.io.jaxb.PlannerBenchmarkConfigIO; +import ai.timefold.solver.benchmark.util.RigidTestdataSolutionFileIO; import ai.timefold.solver.core.impl.io.jaxb.TimefoldXmlSerializationException; import ai.timefold.solver.core.testdomain.TestdataSolution; import ai.timefold.solver.jackson.impl.domain.solution.JacksonSolutionFileIO; -import ai.timefold.solver.persistence.common.api.domain.solution.RigidTestdataSolutionFileIO; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; diff --git a/persistence/common/src/test/java/ai/timefold/solver/persistence/common/api/domain/solution/RigidTestdataSolutionFileIO.java b/tools/benchmark/src/test/java/ai/timefold/solver/benchmark/util/RigidTestdataSolutionFileIO.java similarity index 89% rename from persistence/common/src/test/java/ai/timefold/solver/persistence/common/api/domain/solution/RigidTestdataSolutionFileIO.java rename to tools/benchmark/src/test/java/ai/timefold/solver/benchmark/util/RigidTestdataSolutionFileIO.java index 759cb0b4ea4..4cd93fd674d 100644 --- a/persistence/common/src/test/java/ai/timefold/solver/persistence/common/api/domain/solution/RigidTestdataSolutionFileIO.java +++ b/tools/benchmark/src/test/java/ai/timefold/solver/benchmark/util/RigidTestdataSolutionFileIO.java @@ -1,8 +1,9 @@ -package ai.timefold.solver.persistence.common.api.domain.solution; +package ai.timefold.solver.benchmark.util; import java.io.File; import java.util.Arrays; +import ai.timefold.solver.core.api.domain.solution.SolutionFileIO; import ai.timefold.solver.core.testdomain.TestdataEntity; import ai.timefold.solver.core.testdomain.TestdataSolution; import ai.timefold.solver.core.testdomain.TestdataValue; diff --git a/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/classloaderTestdataBenchmarkConfig.xml b/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/classloaderTestdataBenchmarkConfig.xml index 7e6fb55d2f4..8156946d180 100644 --- a/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/classloaderTestdataBenchmarkConfig.xml +++ b/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/classloaderTestdataBenchmarkConfig.xml @@ -4,7 +4,7 @@ 0 - ai.timefold.solver.persistence.common.api.domain.solution.RigidTestdataSolutionFileIO + ai.timefold.solver.benchmark.util.RigidTestdataSolutionFileIO target/test/benchmarkTest/input.xml diff --git a/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/classloaderTestdataBenchmarkConfigTemplate.xml.ftl b/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/classloaderTestdataBenchmarkConfigTemplate.xml.ftl index 2257a3b10dd..fdf1b1bcef8 100644 --- a/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/classloaderTestdataBenchmarkConfigTemplate.xml.ftl +++ b/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/classloaderTestdataBenchmarkConfigTemplate.xml.ftl @@ -5,7 +5,7 @@ <#list ['FIRST_FIT', 'CHEAPEST_INSERTION'] as constructionHeuristicType> - ai.timefold.solver.persistence.common.api.domain.solution.RigidTestdataSolutionFileIO + ai.timefold.solver.benchmark.util.RigidTestdataSolutionFileIO target/test/benchmarkTest/input.xml diff --git a/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/testdataBenchmarkConfig.xml b/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/testdataBenchmarkConfig.xml index e5b45638ccf..de5d4173203 100644 --- a/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/testdataBenchmarkConfig.xml +++ b/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/testdataBenchmarkConfig.xml @@ -4,7 +4,7 @@ 0 - ai.timefold.solver.persistence.common.api.domain.solution.RigidTestdataSolutionFileIO + ai.timefold.solver.benchmark.util.RigidTestdataSolutionFileIO target/test/benchmarkTest/input.xml diff --git a/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/testdataBenchmarkConfigTemplate.xml.ftl b/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/testdataBenchmarkConfigTemplate.xml.ftl index 98176841ed4..c18bd5136eb 100644 --- a/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/testdataBenchmarkConfigTemplate.xml.ftl +++ b/tools/benchmark/src/test/resources/ai/timefold/solver/benchmark/api/testdataBenchmarkConfigTemplate.xml.ftl @@ -5,7 +5,7 @@ <#list ['FIRST_FIT', 'CHEAPEST_INSERTION'] as constructionHeuristicType> - ai.timefold.solver.persistence.common.api.domain.solution.RigidTestdataSolutionFileIO + ai.timefold.solver.benchmark.util.RigidTestdataSolutionFileIO target/test/benchmarkTest/input.xml diff --git a/tools/migration/pom.xml b/tools/migration/pom.xml index 8c0efa6a2ee..92324ed396f 100644 --- a/tools/migration/pom.xml +++ b/tools/migration/pom.xml @@ -19,10 +19,6 @@ https://solver.timefold.ai - - ai.timefold.solver.migration - - org.openrewrite diff --git a/tools/migration/src/main/java/ai/timefold/solver/migration/one/PersistenceCommonMigrationRecipe.java b/tools/migration/src/main/java/ai/timefold/solver/migration/one/PersistenceCommonMigrationRecipe.java new file mode 100644 index 00000000000..6df0413fe02 --- /dev/null +++ b/tools/migration/src/main/java/ai/timefold/solver/migration/one/PersistenceCommonMigrationRecipe.java @@ -0,0 +1,31 @@ +package ai.timefold.solver.migration.one; + +import java.util.List; + +import ai.timefold.solver.migration.AbstractRecipe; + +import org.openrewrite.Recipe; +import org.openrewrite.java.ChangePackage; +import org.openrewrite.maven.RemoveDependency; + +public class PersistenceCommonMigrationRecipe extends AbstractRecipe { + + @Override + public String getDisplayName() { + return "Migrate away from timefold-solver-persistence-common"; + } + + @Override + public String getDescription() { + return getDisplayName() + "."; + } + + @Override + public List getRecipeList() { + return List.of( + new ChangePackage("ai.timefold.solver.persistence.common.api.domain.solution", + "ai.timefold.solver.core.api.domain.solution", + true), + new RemoveDependency("ai.timefold.solver", "timefold-solver-persistence-common", null)); + } +} diff --git a/tools/migration/src/main/java/ai/timefold/solver/migration/one/TestingAPIsMigrationRecipe.java b/tools/migration/src/main/java/ai/timefold/solver/migration/one/TestingAPIsMigrationRecipe.java index 1b9ca504783..d93c44d43a1 100644 --- a/tools/migration/src/main/java/ai/timefold/solver/migration/one/TestingAPIsMigrationRecipe.java +++ b/tools/migration/src/main/java/ai/timefold/solver/migration/one/TestingAPIsMigrationRecipe.java @@ -7,6 +7,7 @@ import org.openrewrite.Recipe; import org.openrewrite.java.ChangePackage; import org.openrewrite.java.ChangeType; +import org.openrewrite.maven.RemoveDependency; public class TestingAPIsMigrationRecipe extends AbstractRecipe { @@ -34,6 +35,7 @@ public List getRecipeList() { new ChangeType("ai.timefold.solver.core.preview.api.neighborhood.NeighborhoodTester", "ai.timefold.solver.core.preview.api.neighborhood.test.NeighborhoodTester", true), new ChangeType("ai.timefold.solver.core.preview.api.neighborhood.NeighborhoodTestContext", - "ai.timefold.solver.core.preview.api.neighborhood.test.NeighborhoodTestContext", true)); + "ai.timefold.solver.core.preview.api.neighborhood.test.NeighborhoodTestContext", true), + new RemoveDependency("ai.timefold.solver", "timefold-solver-test", null)); } } diff --git a/tools/migration/src/main/java/module-info.java b/tools/migration/src/main/java/module-info.java new file mode 100644 index 00000000000..91a13f4efcd --- /dev/null +++ b/tools/migration/src/main/java/module-info.java @@ -0,0 +1,10 @@ +module ai.timefold.solver.migration { + + exports ai.timefold.solver.migration; + exports ai.timefold.solver.migration.one; + + requires rewrite.core; + requires rewrite.java; + requires rewrite.maven; + +} \ No newline at end of file diff --git a/tools/migration/src/main/resources/META-INF/rewrite/ToLatest.yml b/tools/migration/src/main/resources/META-INF/rewrite/ToLatest.yml index e5bf461c725..33ea3a622cc 100644 --- a/tools/migration/src/main/resources/META-INF/rewrite/ToLatest.yml +++ b/tools/migration/src/main/resources/META-INF/rewrite/ToLatest.yml @@ -9,8 +9,6 @@ recipeList: - org.openrewrite.java.DeleteMethodArgument: methodPattern: ai.timefold.solver.core.api.score.constraint.ConstraintRef of(String, String) argumentIndex: 0 + - ai.timefold.solver.migration.one.PersistenceCommonMigrationRecipe - ai.timefold.solver.migration.one.TestingAPIsMigrationRecipe - - org.openrewrite.java.RemoveUnusedImports - - org.openrewrite.maven.RemoveDependency: - groupId: ai.timefold.solver - artifactId: timefold-solver-test + - org.openrewrite.java.RemoveUnusedImports \ No newline at end of file diff --git a/tools/migration/src/test/java/ai/timefold/solver/migration/one/PersistenceCommonMigrationRecipeTest.java b/tools/migration/src/test/java/ai/timefold/solver/migration/one/PersistenceCommonMigrationRecipeTest.java new file mode 100644 index 00000000000..618556fc43a --- /dev/null +++ b/tools/migration/src/test/java/ai/timefold/solver/migration/one/PersistenceCommonMigrationRecipeTest.java @@ -0,0 +1,60 @@ +package ai.timefold.solver.migration.one; + +import static org.openrewrite.java.Assertions.java; + +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.openrewrite.java.JavaParser; +import org.openrewrite.java.style.ImportLayoutStyle; +import org.openrewrite.style.NamedStyles; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.TypeValidation; + +@Execution(ExecutionMode.CONCURRENT) +class PersistenceCommonMigrationRecipeTest implements RewriteTest { + + private static final class NoWildCardImportStyle extends NamedStyles { + + public NoWildCardImportStyle() { + super(UUID.randomUUID(), "ImportStyle", "ImportStyle", "ImportStyle", Collections.emptySet(), + List.of(ImportLayoutStyle.builder().classCountToUseStarImport(9999999).importStaticAllOthers() + .importAllOthers().build())); + } + } + + @Override + public void defaults(RecipeSpec spec) { + spec.recipes(new PersistenceCommonMigrationRecipe()) + .typeValidationOptions(TypeValidation.builder().allowMissingType(ignore -> true).build()) + .parser(JavaParser.fromJavaVersion().styles(List.of(new NoWildCardImportStyle())) + // We must add all old classes as stubs to the JavaTemplate + .dependsOn( + "package ai.timefold.solver.persistence.common.api.domain.solution; public class SolutionFileIO {}")); + } + + @Test + void migrate() { + rewriteRun(java(""" + package timefold; + + import ai.timefold.solver.persistence.common.api.domain.solution.SolutionFileIO; + + public class Test { + SolutionFileIO solutionFileIO; + }""", """ + package timefold; + + import ai.timefold.solver.core.api.domain.solution.SolutionFileIO; + + public class Test { + SolutionFileIO solutionFileIO; + }""")); + } + +}