我正在创建一个学校时间表生成器,并且我从我的一个约束中得到一个异常,该约束检查所有学生每天都有午休时间。
我使用约束流Java API。我的约束编译(显然)看起来像这样:
public Constraint studentsShouldHaveLunchEveryDay(ConstraintFactory cf) {
return cf.from(RecurringLecture.class)
.filter(RecurringLecture::isScheduled) // [Lecture]...
.groupBy(l -> l, l -> l.getCourseRound().getStudents()) // [Lecture, Set<Student>]...
.flattenLast(students -> students) // [Lecture, Student]...
.groupBy((l, s) -> s, // [Student, Map<Day, Set<Lecture>>]...
ConstraintCollectors.toMap(
(l, s) -> l.getStartTimeslot().getDay(),
(l, s) -> l))
//.flattenLast(Map::values) // [Student, Set<Lecture>]...
//.filter((student, dailyLectures) -> !LunchBreakAnalyzer.hasLunchBreak(dailyLectures))
.penalize(StudentLunchProblem.class.getName(), HardSoftScore.ONE_HARD);
}
由于调试原因,我暂时注释掉了flattenLast
和filter
,但问题仍然出现。如果我注释掉最后一个groupBy
,这个问题似乎不会重现。
由于某种原因,框架试图将Student
(确切地说是ImmutableStudent
)转换为Object[]
。
框架中抛出的行在ArrayElementReader
中,看起来像这样:
public Object getValue(InternalWorkingMemory workingMemory,
Object object) {
Object[] array = (Object[]) this.arrayReadAccessor.getValue( workingMemory,
object );
return array[this.index];
}
完整的异常如下:
java.util.concurrent.ExecutionException: java.lang.IllegalStateException: The move thread with moveThreadIndex (1) has thrown an exception. Relayed here in the parent thread.
at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
at java.desktop/javax.swing.SwingWorker.get(SwingWorker.java:613)
at vngschedules.ui.automation.AutomationPanel$1$1.done(AutomationPanel.java:44)
at java.desktop/javax.swing.SwingWorker$5.run(SwingWorker.java:750)
at java.desktop/javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.run(SwingWorker.java:847)
at java.desktop/sun.swing.AccumulativeRunnable.run(AccumulativeRunnable.java:112)
at java.desktop/javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.actionPerformed(SwingWorker.java:857)
at java.desktop/javax.swing.Timer.fireActionPerformed(Timer.java:317)
at java.desktop/javax.swing.Timer$DoPostEvent.run(Timer.java:249)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Caused by: java.lang.IllegalStateException: The move thread with moveThreadIndex (1) has thrown an exception. Relayed here in the parent thread.
at org.optaplanner.core.impl.heuristic.thread.OrderByMoveIndexBlockingQueue.take(OrderByMoveIndexBlockingQueue.java:147)
at org.optaplanner.core.impl.localsearch.decider.MultiThreadedLocalSearchDecider.forageResult(MultiThreadedLocalSearchDecider.java:189)
at org.optaplanner.core.impl.localsearch.decider.MultiThreadedLocalSearchDecider.decideNextStep(MultiThreadedLocalSearchDecider.java:160)
at org.optaplanner.core.impl.localsearch.DefaultLocalSearchPhase.solve(DefaultLocalSearchPhase.java:95)
at org.optaplanner.core.impl.solver.AbstractSolver.runPhases(AbstractSolver.java:99)
at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:192)
Caused by: java.lang.IllegalStateException: The move thread with moveThreadIndex (1) has thrown an exception. Relayed here in the parent thread.
at vngschedules.ui.automation.SolverSwingWorker.doInBackground(SolverSwingWorker.java:29)
at vngschedules.ui.automation.SolverSwingWorker.doInBackground(SolverSwingWorker.java:10)
at java.desktop/javax.swing.SwingWorker$1.call(SwingWorker.java:304)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.desktop/javax.swing.SwingWorker.run(SwingWorker.java:343)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.ClassCastException: class vngschedules.schedule.ImmutableStudent cannot be cast to class [Ljava.lang.Object; (vngschedules.schedule.ImmutableStudent is in unnamed module of loader 'app'; [Ljava.lang.Object; is in module java.base of loader 'bootstrap')
at org.drools.core.base.extractors.ArrayElementReader.getValue(ArrayElementReader.java:162)
at org.drools.core.base.extractors.ArrayElementReader.getValue(ArrayElementReader.java:279)
at org.drools.modelcompiler.KiePackagesBuilder.lambda$getBindingFunction$66de2761$1(KiePackagesBuilder.java:794)
at org.drools.modelcompiler.constraints.LambdaReadAccessor.getValue(LambdaReadAccessor.java:43)
at org.drools.core.rule.Declaration.getValue(Declaration.java:257)
at org.optaplanner.core.impl.score.stream.drools.common.AbstractAccumulator.extractValue(AbstractAccumulator.java:42)
Caused by: java.lang.ClassCastException: class vngschedules.schedule.ImmutableStudent cannot be cast to class [Ljava.lang.Object; (vngschedules.schedule.ImmutableStudent is in unnamed module of loader 'app'; [Ljava.lang.Object; is in module java.base of loader 'bootstrap')
at org.optaplanner.core.impl.score.stream.drools.common.BiAccumulator.accumulate(BiAccumulator.java:55)
at org.drools.core.rule.SingleAccumulate.accumulate(SingleAccumulate.java:96)
at org.drools.modelcompiler.constraints.LambdaGroupByAccumulate.accumulate(LambdaGroupByAccumulate.java:121)
at org.drools.modelcompiler.constraints.LambdaGroupByAccumulate.accumulate(LambdaGroupByAccumulate.java:114)
at org.drools.core.phreak.PhreakAccumulateNode.addMatch(PhreakAccumulateNode.java:736)
at org.drools.core.phreak.PhreakAccumulateNode.doRightInserts(PhreakAccumulateNode.java:253)
at org.drools.core.phreak.PhreakAccumulateNode.doNode(PhreakAccumulateNode.java:99)
at org.drools.core.phreak.RuleNetworkEvaluator.switchOnDoBetaNode(RuleNetworkEvaluator.java:586)
at org.drools.core.phreak.RuleNetworkEvaluator.evalBetaNode(RuleNetworkEvaluator.java:555)
at org.drools.core.phreak.RuleNetworkEvaluator.evalNode(RuleNetworkEvaluator.java:382)
at org.drools.core.phreak.RuleNetworkEvaluator.innerEval(RuleNetworkEvaluator.java:342)
at org.drools.core.phreak.RuleNetworkEvaluator.evalStackEntry(RuleNetworkEvaluator.java:240)
at org.drools.core.phreak.RuleNetworkEvaluator.outerEval(RuleNetworkEvaluator.java:183)
at org.drools.core.phreak.RuleNetworkEvaluator.evaluateNetwork(RuleNetworkEvaluator.java:136)
at org.drools.core.phreak.RuleExecutor.reEvaluateNetwork(RuleExecutor.java:235)
at org.drools.core.phreak.RuleExecutor.evaluateNetworkAndFire(RuleExecutor.java:91)
at org.drools.core.concurrent.AbstractRuleEvaluator.internalEvaluateAndFire(AbstractRuleEvaluator.java:33)
at org.drools.core.concurrent.SequentialRuleEvaluator.evaluateAndFire(SequentialRuleEvaluator.java:43)
at org.drools.core.common.DefaultAgenda.fireLoop(DefaultAgenda.java:869)
at org.drools.core.common.DefaultAgenda.internalFireAllRules(DefaultAgenda.java:816)
at org.drools.core.common.DefaultAgenda.fireAllRules(DefaultAgenda.java:808)
at org.drools.core.impl.StatefulKnowledgeSessionImpl.internalFireAllRules(StatefulKnowledgeSessionImpl.java:1343)
at org.drools.core.impl.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1334)
at org.drools.core.impl.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1326)
at org.optaplanner.core.impl.score.director.stream.DroolsConstraintStreamScoreDirector.calculateScore(DroolsConstraintStreamScoreDirector.java:90)
at org.optaplanner.core.impl.score.director.AbstractScoreDirector.doAndProcessMove(AbstractScoreDirector.java:220)
at org.optaplanner.core.impl.heuristic.thread.MoveThreadRunner.run(MoveThreadRunner.java:147)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
... 3 more
我已经注释掉了程序中所有其他的约束流。
- 这看起来像这是一个错误在我的代码,或者它是一个问题与OptaPlanner?
- 如果它在我的代码中,有什么技巧来调试这个?我怀疑我的约束被编译成口水之类的东西。是否有一种方法可以跳过此编译,以便可以将异常追溯到我的代码?(到目前为止,没有任何异常行可以追溯到我的代码。)
如果需要的话,我很乐意提供更多的类型,但是RecurringLecture
真的很简单,Student
是一个编译成ImmutableStudent
的不可变接口。
我使用OptaPlanner 8.12.0。Final和Java 11.
更新我尝试重写约束流如下:
public Constraint studentsShouldHaveLunchEveryDay(ConstraintFactory cf) {
return cf.from(RecurringLecture.class)
.filter(RecurringLecture::isScheduled) // [Lecture]...
.groupBy(l -> l, l -> l.getCourseRound().getStudents()) // [Lecture, Set<Student>]...
.flattenLast(students -> students) // [Lecture, Student]...
.groupBy((l, s) -> ImmutableStudentAndDay.of(s, l.getStartTimeslot().getDay()),
ConstraintCollectors.toSet((l, s) -> l))
.filter((student, dailyLectures) -> !LunchBreakAnalyzer.hasLunchBreak(dailyLectures))
.penalize(StudentLunchProblem.class.getName(), HardSoftScore.ONE_HARD);
}
这避免了toMap
收集器。但是,我仍然遇到完全相同的问题。
一般来说,如果您的约束编译并且代码在运行时仍然抛出ClassCastException
,那么您应该期望该错误在OptaPlanner端。除非您愿意查看Drools可执行模型,否则没有什么可调试的。
我建议这样重构约束:
public Constraint studentsShouldHaveLunchEveryDay(ConstraintFactory cf) {
return cf.from(RecurringLecture.class)
.filter(RecurringLecture::isScheduled)
.join(Student.class,
Joiners.filtering((l, s) -> l.getCourseRound().getStudents().contains(s)))
.groupBy((l, s) -> ImmutableStudentAndDay.of(s, l.getStartTimeslot().getDay()),
ConstraintCollectors.toSet((l, s) -> l))
.filter((student, dailyLectures) -> !LunchBreakAnalyzer.hasLunchBreak(dailyLectures))
.penalize(StudentLunchProblem.class.getName(), HardSoftScore.ONE_HARD);
}
我希望这能执行得更好,可能还能摆脱异常。无论如何,异常仍然是一个问题,如果你提供简化的可执行复制器,我将进一步调查它。