Recently learning Optaplanner constraint framework, but I can't understand the shadow variables, the documentation only has some simple descriptions, hope someone can help me, here is my code,thanks.
@Data
@PlanningEntity
public class CloudProcess {
private int requireCpuPower;
private int requireMemory;
private int requireNetworkBandwidth;
@PlanningVariable(valueRangeProviderRefs = {"computerRange"})
private CloudComputer cloudComputer;
}
@Data
@PlanningEntity
public class CloudComputer {
private int cpuPower;
private int memory;
private int networkBandwidth;
private int cost;
@InverseRelationShadowVariable(sourceVariableName = "cloudComputer")
private List<CloudProcess> processList = new ArrayList<>();
}
@PlanningSolution
@Data
public class CloudBalance {
@PlanningEntityCollectionProperty
private List<CloudProcess> processList;
@ProblemFactCollectionProperty
@ValueRangeProvider(id="computerRange")
private List<CloudComputer> computerList;
@PlanningScore
private HardSoftScore score;
}
java.lang.StackOverflowError: null at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na] at java.base/java.util.ArrayList.hashCodeRange(ArrayList.java:626) ~[na:na] at java.base/java.util.ArrayList.hashCode(ArrayList.java:613) ~[na:na] at com.zxx.model.cloudBalancing.CloudComputer.hashCode(CloudComputer.java:13) ~[classes/:na] at com.zxx.model.cloudBalancing.CloudProcess.hashCode(CloudProcess.java:7) ~[classes/:na]
The java.lang.StackOverflowError
is caused by combination of two facts:
@InverseRelationShadowVariable processList
on the CloudComputer
you are creating a bi-directional relationship between CloudComputer
and CloudProcess
.@Data
, which generates equals()
, hashCode()
and toString()
implementations that use all the class fields by default.That leads to an infinite loop because, for example, to compute Process 1
's hash code, Computer A
's hash code is needed and that includes its processList
hashCode, which contains Computer A
and so on. It's impossible to compute the hash code and the method will run until the current thread's stack overflows with local variables holding the partial results of the computation.
If you don't need Lombok-generated hashCode()
, equals()
and toString()
, then replace @Data
with @Getter
and @Setter
.
If you want all the code generated by @Data
including hashCode()
, equals()
and toString()
, then you must replace it with more annotations and exclude the computer
and processList
fields to break the loop:
@PlanningEntity
@Getter
@Setter
@ToString
@EqualsAndHashCode(callSuper = true, exclude = "computer")
public class CloudProcess {
private int requiredCpuPower;
private int requiredMemory;
private int requiredNetworkBandwidth;
@PlanningVariable(valueRangeProviderRefs = {"computerRange"})
private CloudComputer computer;
}
@PlanningEntity
@Getter
@Setter
@ToString(exclude = "processList")
@EqualsAndHashCode(callSuper = true, exclude = "processList")
public class CloudComputer {
private int cpuPower;
private int memory;
private int networkBandwidth;
private int cost;
@InverseRelationShadowVariable(sourceVariableName = "computer")
private List<CloudProcess> processList = new ArrayList<>();
}