From 4e0ea2972f419e8557cc4b29a03c61da153be1be Mon Sep 17 00:00:00 2001 From: dupliaka Date: Thu, 18 Aug 2022 06:32:25 +0200 Subject: [PATCH] Update optaplanner example (#12467) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update optaplanner example Moved to 8.24.0.Final version Removed deprecated drools calculation Added constraints stream solver Updated to solver config java api * Update optaplanner/pom.xml Co-authored-by: Jiří Locker * PR feedback * Move OptaPlanner module to jdk9-and-above Moved to default-jdk9-and-above and integration-jdk9-and-above Co-authored-by: Jiří Locker --- optaplanner/pom.xml | 8 ++++- .../baeldung/optaplanner/CourseSchedule.java | 28 ++++++++++----- .../CourseScheduleConstraintProvider.java | 24 +++++++++++++ .../com/baeldung/optaplanner/Lecture.java | 10 ++++++ .../baeldung/optaplanner/ScoreCalculator.java | 9 +++-- .../src/main/resources/courseSchedule.drl | 14 -------- .../courseScheduleSolverConfigDrools.xml | 12 ------- .../courseScheduleSolverConfiguration.xml | 12 ------- optaplanner/src/main/resources/logback.xml | 15 +++++++- ...rseScheduleConstraintProviderUnitTest.java | 28 +++++++++++++++ .../optaplanner/test/OptaPlannerUnitTest.java | 36 +++++++++++++++---- pom.xml | 4 +-- 12 files changed, 139 insertions(+), 61 deletions(-) create mode 100644 optaplanner/src/main/java/com/baeldung/optaplanner/CourseScheduleConstraintProvider.java delete mode 100644 optaplanner/src/main/resources/courseSchedule.drl delete mode 100644 optaplanner/src/main/resources/courseScheduleSolverConfigDrools.xml delete mode 100644 optaplanner/src/main/resources/courseScheduleSolverConfiguration.xml create mode 100644 optaplanner/src/test/java/com/baeldung/optaplanner/test/CourseScheduleConstraintProviderUnitTest.java diff --git a/optaplanner/pom.xml b/optaplanner/pom.xml index 0e5fa9050e..e1653fd749 100644 --- a/optaplanner/pom.xml +++ b/optaplanner/pom.xml @@ -18,10 +18,16 @@ optaplanner-core ${optaplanner-core.version} + + org.optaplanner + optaplanner-test + ${optaplanner-core.version} + test + - 7.9.0.Final + 8.24.0.Final \ No newline at end of file diff --git a/optaplanner/src/main/java/com/baeldung/optaplanner/CourseSchedule.java b/optaplanner/src/main/java/com/baeldung/optaplanner/CourseSchedule.java index 8a820ab56e..357dfe809f 100644 --- a/optaplanner/src/main/java/com/baeldung/optaplanner/CourseSchedule.java +++ b/optaplanner/src/main/java/com/baeldung/optaplanner/CourseSchedule.java @@ -3,7 +3,7 @@ package com.baeldung.optaplanner; import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty; import org.optaplanner.core.api.domain.solution.PlanningScore; import org.optaplanner.core.api.domain.solution.PlanningSolution; -import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty; +import org.optaplanner.core.api.domain.solution.ProblemFactCollectionProperty; import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider; import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore; import org.slf4j.Logger; @@ -16,36 +16,36 @@ import java.util.List; public class CourseSchedule { Logger logger = LoggerFactory.getLogger("CourseSchedule"); - + @ValueRangeProvider(id = "availableRooms") + @ProblemFactCollectionProperty private List roomList; + @ValueRangeProvider(id = "availablePeriods") + @ProblemFactCollectionProperty private List periodList; + @PlanningEntityCollectionProperty private List lectureList; + @PlanningScore private HardSoftScore score; + public CourseSchedule(){ roomList = new ArrayList<>(); periodList = new ArrayList<>(); lectureList = new ArrayList<>(); } - @ValueRangeProvider(id = "availableRooms") - @ProblemFactCollectionProperty public List getRoomList() { return roomList; } - @ValueRangeProvider(id = "availablePeriods") - @ProblemFactCollectionProperty public List getPeriodList() { return periodList; } - @PlanningEntityCollectionProperty public List getLectureList() { return lectureList; } - @PlanningScore public HardSoftScore getScore() { return score; } @@ -54,6 +54,18 @@ public class CourseSchedule { this.score = score; } + public void setRoomList(List roomList) { + this.roomList = roomList; + } + + public void setPeriodList(List periodList) { + this.periodList = periodList; + } + + public void setLectureList(List lectureList) { + this.lectureList = lectureList; + } + public void printCourseSchedule() { lectureList.stream() .map(c -> "Lecture in Room " + c.getRoomNumber().toString() + " during Period " + c.getPeriod().toString()) diff --git a/optaplanner/src/main/java/com/baeldung/optaplanner/CourseScheduleConstraintProvider.java b/optaplanner/src/main/java/com/baeldung/optaplanner/CourseScheduleConstraintProvider.java new file mode 100644 index 0000000000..2733806d16 --- /dev/null +++ b/optaplanner/src/main/java/com/baeldung/optaplanner/CourseScheduleConstraintProvider.java @@ -0,0 +1,24 @@ +package com.baeldung.optaplanner; + +import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore; +import org.optaplanner.core.api.score.stream.Constraint; +import org.optaplanner.core.api.score.stream.ConstraintFactory; +import org.optaplanner.core.api.score.stream.ConstraintProvider; +import org.optaplanner.core.api.score.stream.Joiners; + +public class CourseScheduleConstraintProvider implements ConstraintProvider { + + @Override + public Constraint[] defineConstraints(ConstraintFactory constraintFactory) { + return new Constraint[]{ + roomConflict(constraintFactory) + }; + } + + public Constraint roomConflict(ConstraintFactory constraintFactory){ + return constraintFactory + .forEachUniquePair(Lecture.class, + Joiners.equal(Lecture::getRoomNumber), Joiners.equal(Lecture::getPeriod)) + .penalize("Room conflict", HardSoftScore.ONE_HARD); + } +} diff --git a/optaplanner/src/main/java/com/baeldung/optaplanner/Lecture.java b/optaplanner/src/main/java/com/baeldung/optaplanner/Lecture.java index 48af4e89d1..b63bd21030 100644 --- a/optaplanner/src/main/java/com/baeldung/optaplanner/Lecture.java +++ b/optaplanner/src/main/java/com/baeldung/optaplanner/Lecture.java @@ -1,14 +1,24 @@ package com.baeldung.optaplanner; import org.optaplanner.core.api.domain.entity.PlanningEntity; +import org.optaplanner.core.api.domain.lookup.PlanningId; import org.optaplanner.core.api.domain.variable.PlanningVariable; @PlanningEntity public class Lecture { + @PlanningId + private Long id; private Integer roomNumber; private Integer period; + public Lecture(Long i) { + this.id = i; + } + + public Lecture() { + } + @PlanningVariable(valueRangeProviderRefs = {"availablePeriods"}) public Integer getPeriod() { return period; diff --git a/optaplanner/src/main/java/com/baeldung/optaplanner/ScoreCalculator.java b/optaplanner/src/main/java/com/baeldung/optaplanner/ScoreCalculator.java index 11e5e4a5be..3e3747f764 100644 --- a/optaplanner/src/main/java/com/baeldung/optaplanner/ScoreCalculator.java +++ b/optaplanner/src/main/java/com/baeldung/optaplanner/ScoreCalculator.java @@ -1,15 +1,14 @@ package com.baeldung.optaplanner; -import org.optaplanner.core.api.score.Score; import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore; -import org.optaplanner.core.impl.score.director.easy.EasyScoreCalculator; +import org.optaplanner.core.api.score.calculator.EasyScoreCalculator; import java.util.HashSet; -public class ScoreCalculator implements EasyScoreCalculator { +public class ScoreCalculator implements EasyScoreCalculator { @Override - public Score calculateScore(CourseSchedule courseSchedule) { + public HardSoftScore calculateScore(CourseSchedule courseSchedule) { int hardScore = 0; int softScore = 0; @@ -27,6 +26,6 @@ public class ScoreCalculator implements EasyScoreCalculator { } } - return HardSoftScore.valueOf(hardScore, softScore); + return HardSoftScore.of(hardScore, softScore); } } diff --git a/optaplanner/src/main/resources/courseSchedule.drl b/optaplanner/src/main/resources/courseSchedule.drl deleted file mode 100644 index 9372a24976..0000000000 --- a/optaplanner/src/main/resources/courseSchedule.drl +++ /dev/null @@ -1,14 +0,0 @@ -package com.baeldung.optaplanner - -import com.baeldung.optaplanner.Lecture; -import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScoreHolder; - -global HardSoftScoreHolder scoreHolder; - -rule "noNullRoomPeriod" - when - Lecture( roomNumber == null ); - Lecture( period == null ); - then - scoreHolder.addHardConstraintMatch(kcontext, -1); -end \ No newline at end of file diff --git a/optaplanner/src/main/resources/courseScheduleSolverConfigDrools.xml b/optaplanner/src/main/resources/courseScheduleSolverConfigDrools.xml deleted file mode 100644 index 7cf95fdcd3..0000000000 --- a/optaplanner/src/main/resources/courseScheduleSolverConfigDrools.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - courseSchedule.drl - - - - 10 - - \ No newline at end of file diff --git a/optaplanner/src/main/resources/courseScheduleSolverConfiguration.xml b/optaplanner/src/main/resources/courseScheduleSolverConfiguration.xml deleted file mode 100644 index 0ad2264dcd..0000000000 --- a/optaplanner/src/main/resources/courseScheduleSolverConfiguration.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - com.baeldung.optaplanner.ScoreCalculator - - - - 10 - - \ No newline at end of file diff --git a/optaplanner/src/main/resources/logback.xml b/optaplanner/src/main/resources/logback.xml index c109aa19e2..34da901079 100644 --- a/optaplanner/src/main/resources/logback.xml +++ b/optaplanner/src/main/resources/logback.xml @@ -1,3 +1,16 @@ + + + + + %d{HH:mm:ss.SSS} [%-12.12t] %-5p %m%n + + + - \ No newline at end of file + + + + + + diff --git a/optaplanner/src/test/java/com/baeldung/optaplanner/test/CourseScheduleConstraintProviderUnitTest.java b/optaplanner/src/test/java/com/baeldung/optaplanner/test/CourseScheduleConstraintProviderUnitTest.java new file mode 100644 index 0000000000..22de095ce9 --- /dev/null +++ b/optaplanner/src/test/java/com/baeldung/optaplanner/test/CourseScheduleConstraintProviderUnitTest.java @@ -0,0 +1,28 @@ +package com.baeldung.optaplanner.test; + +import com.baeldung.optaplanner.CourseSchedule; +import com.baeldung.optaplanner.CourseScheduleConstraintProvider; +import com.baeldung.optaplanner.Lecture; +import org.junit.jupiter.api.Test; +import org.optaplanner.test.api.score.stream.ConstraintVerifier; + +public class CourseScheduleConstraintProviderUnitTest { + + private final ConstraintVerifier constraintVerifier = + ConstraintVerifier.build(new CourseScheduleConstraintProvider(), CourseSchedule.class, Lecture.class); + + @Test + public void testCourseScheduleConstraint() { + + Lecture lecture = new Lecture(0L); + lecture.setPeriod(1); + lecture.setRoomNumber(1); + Lecture conflictedLecture = new Lecture(1L); + conflictedLecture.setPeriod(1); + conflictedLecture.setRoomNumber(1); + + constraintVerifier.verifyThat(CourseScheduleConstraintProvider::roomConflict) + .given(lecture, conflictedLecture) + .penalizesBy(1); + } +} diff --git a/optaplanner/src/test/java/com/baeldung/optaplanner/test/OptaPlannerUnitTest.java b/optaplanner/src/test/java/com/baeldung/optaplanner/test/OptaPlannerUnitTest.java index 2847a233a5..2b72765074 100644 --- a/optaplanner/src/test/java/com/baeldung/optaplanner/test/OptaPlannerUnitTest.java +++ b/optaplanner/src/test/java/com/baeldung/optaplanner/test/OptaPlannerUnitTest.java @@ -1,16 +1,26 @@ package com.baeldung.optaplanner.test; import com.baeldung.optaplanner.CourseSchedule; +import com.baeldung.optaplanner.CourseScheduleConstraintProvider; import com.baeldung.optaplanner.Lecture; +import com.baeldung.optaplanner.ScoreCalculator; import org.junit.Assert; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.optaplanner.core.api.score.ScoreManager; import org.optaplanner.core.api.solver.Solver; import org.optaplanner.core.api.solver.SolverFactory; +import org.optaplanner.core.config.solver.SolverConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; import java.util.Arrays; public class OptaPlannerUnitTest { + Logger logger = LoggerFactory.getLogger("OptaPlannerUnitTest"); + static CourseSchedule unsolvedCourseSchedule; @BeforeAll @@ -18,8 +28,8 @@ public class OptaPlannerUnitTest { unsolvedCourseSchedule = new CourseSchedule(); - for(int i = 0; i < 10; i++){ - unsolvedCourseSchedule.getLectureList().add(new Lecture()); + for(long i = 0; i < 10; i++) { + unsolvedCourseSchedule.getLectureList().add(new Lecture(i)); } unsolvedCourseSchedule.getPeriodList().addAll(Arrays.asList(new Integer[] { 1, 2, 3 })); @@ -29,7 +39,12 @@ public class OptaPlannerUnitTest { @Test public void test_whenCustomJavaSolver() { - SolverFactory solverFactory = SolverFactory.createFromXmlResource("courseScheduleSolverConfiguration.xml"); + SolverFactory solverFactory = SolverFactory.create(new SolverConfig() + .withSolutionClass(CourseSchedule.class) + .withEntityClasses(Lecture.class) + .withEasyScoreCalculatorClass(ScoreCalculator.class) + .withTerminationSpentLimit(Duration.ofSeconds(1))); + Solver solver = solverFactory.buildSolver(); CourseSchedule solvedCourseSchedule = solver.solve(unsolvedCourseSchedule); @@ -38,13 +53,22 @@ public class OptaPlannerUnitTest { } @Test - public void test_whenDroolsSolver() { + public void test_whenConstraintStreamSolver() { + + SolverFactory solverFactory = SolverFactory.create(new SolverConfig() + .withSolutionClass(CourseSchedule.class) + .withEntityClasses(Lecture.class) + .withConstraintProviderClass(CourseScheduleConstraintProvider.class) + .withTerminationSpentLimit(Duration.ofSeconds(1))); - SolverFactory solverFactory = SolverFactory.createFromXmlResource("courseScheduleSolverConfigDrools.xml"); Solver solver = solverFactory.buildSolver(); CourseSchedule solvedCourseSchedule = solver.solve(unsolvedCourseSchedule); + logger.info(ScoreManager.create(solverFactory).explainScore(solvedCourseSchedule).getSummary()); + Assert.assertNotNull(solvedCourseSchedule.getScore()); - Assert.assertEquals(0, solvedCourseSchedule.getScore().getHardScore()); + Assert.assertEquals(-4, solvedCourseSchedule.getScore().getHardScore()); + } + } diff --git a/pom.xml b/pom.xml index 3918250d85..da9a18bd3d 100644 --- a/pom.xml +++ b/pom.xml @@ -492,7 +492,6 @@ open-liberty oauth2-framework-impl - optaplanner orika osgi @@ -926,7 +925,6 @@ open-liberty oauth2-framework-impl - optaplanner orika osgi @@ -1248,6 +1246,7 @@ apache-httpclient-2 libraries-concurrency maven-modules/multimodulemavenproject + optaplanner persistence-modules/sirix persistence-modules/spring-data-cassandra-2 quarkus-modules/quarkus-vs-springboot @@ -1317,6 +1316,7 @@ apache-httpclient-2 libraries-concurrency maven-modules/multimodulemavenproject + optaplanner persistence-modules/sirix persistence-modules/spring-data-cassandra-2 quarkus-modules/quarkus-vs-springboot