I'm trying to use <domainAccessType>GIZMO</domainAccessType>
in my solver config.
It seems to get fast access with most of my variables, but it throws an exception for variables in my @PlanningSolution
:
17:30:17.863 [main ] TRACE Model annotations parsed for solution VehicleRoutingSolution:
17:30:17.866 [main ] TRACE Entity Standstill:
17:30:17.866 [main ] TRACE Shadow variable nextVisit (Fast access with generated bytecode)
17:30:17.866 [main ] TRACE Entity Visit:
17:30:17.866 [main ] TRACE Genuine variable previousStandstill (Fast access with generated bytecode)
17:30:17.866 [main ] TRACE Shadow variable arrivalTime (Fast access with generated bytecode)
17:30:17.866 [main ] TRACE Shadow variable subShift (Fast access with generated bytecode)
Exception in thread "main" java.lang.IllegalStateException: Member (solverStatus) of class (org.acme.domain.VehicleRoutingSolution) is not public and domainAccessType is GIZMO.
Maybe put the annotations onto the public getter of the field.
Maybe use domainAccessType REFLECTION instead of GIZMO.
at org.optaplanner.core.impl.domain.common.accessor.gizmo.GizmoMemberDescriptor.<init>(GizmoMemberDescriptor.java:79)
at org.optaplanner.core.impl.domain.solution.cloner.gizmo.GizmoSolutionOrEntityDescriptor.getFieldsToSolutionFieldToMemberDescriptorMap(GizmoSolutionOrEntityDescriptor.java:63)
at org.optaplanner.core.impl.domain.solution.cloner.gizmo.GizmoSolutionOrEntityDescriptor.<init>(GizmoSolutionOrEntityDescriptor.java:39)
at org.optaplanner.core.impl.domain.solution.cloner.gizmo.GizmoSolutionClonerImplementor.lambda$createCloneSolutionRun$6(GizmoSolutionClonerImplementor.java:294)
at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1220)
at org.optaplanner.core.impl.domain.solution.cloner.gizmo.GizmoSolutionClonerImplementor.createCloneSolutionRun(GizmoSolutionClonerImplementor.java:293)
at org.optaplanner.core.impl.domain.solution.cloner.gizmo.GizmoSolutionClonerImplementor.defineClonerFor(GizmoSolutionClonerImplementor.java:157)
at org.optaplanner.core.impl.domain.solution.cloner.gizmo.GizmoSolutionClonerImplementor.createClonerFor(GizmoSolutionClonerImplementor.java:200)
at org.optaplanner.core.impl.domain.solution.cloner.gizmo.GizmoSolutionClonerFactory.build(GizmoSolutionClonerFactory.java:31)
at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.initSolutionCloner(SolutionDescriptor.java:601)
at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.afterAnnotationsProcessed(SolutionDescriptor.java:545)
at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.buildSolutionDescriptor(SolutionDescriptor.java:126)
at org.optaplanner.core.impl.solver.DefaultSolverFactory.buildSolutionDescriptor(DefaultSolverFactory.java:160)
at org.optaplanner.core.impl.solver.DefaultSolverFactory.buildScoreDirectorFactory(DefaultSolverFactory.java:133)
at org.optaplanner.core.impl.solver.DefaultSolverFactory.buildSolver(DefaultSolverFactory.java:87)
at org.optaplanner.core.impl.solver.DefaultSolverManager.validateSolverFactory(DefaultSolverManager.java:69)
at org.optaplanner.core.impl.solver.DefaultSolverManager.<init>(DefaultSolverManager.java:58)
at org.optaplanner.core.api.solver.SolverManager.create(SolverManager.java:111)
at org.optaplanner.core.api.solver.SolverManager.create(SolverManager.java:84)
Here is the Planning Solution I'm using:
@PlanningSolution
class VehicleRoutingSolution {
@PlanningEntityCollectionProperty
@ValueRangeProvider(id = "visitRange")
lateinit var visitList: List<Visit>
@PlanningEntityCollectionProperty
@ValueRangeProvider(id = "subShiftRange")
lateinit var subShiftList: List<SubShift>
private var solverStatus: SolverStatus? = null
fun getSolverStatus(): SolverStatus? {
return solverStatus
}
fun setSolverStatus(solverStatus: SolverStatus?) {
this.solverStatus = solverStatus
}
private var score: SimpleLongScore? = null
@PlanningScore
fun getScore(): SimpleLongScore? {
return score
}
fun setScore(score: SimpleLongScore?) {
this.score = score
}
// No-arg constructor required for OptaPlanner
constructor() {}
constructor(subShiftList: List<SubShift>, visitList: List<Visit>) {
this.subShiftList = subShiftList
this.visitList = visitList
}
}
The solverStatus
doesn't even have an annotation, so I don't understand why it's complaining about that.
And oddly, if I put my score
variable section above my solverStatus
section, it complains about score
not being public
when the getter clearly is.
Any idea what's going on here?
Unable to reproduce in Quarkus. See https://github.com/kiegroup/optaplanner-quickstarts/tree/stable/technology/kotlin-quarkus for an example of using OptaPlanner with Kotlin in Quarkus. I tested changing the the fields of TimeTable to private, and it works as expected.
Outside of Quarkus, this behaviour is expected. When domain access type GIZMO is used, member accessors and a custom solution cloner is generated for the domain. The custom solution cloner requires all fields to be public; public getters/setters are not used since they are not guaranteed to be simple (they could do validation checks that throw an exception, or modify other fields).