CustomChangeMove in chained planning entities throws IllegalStateException (Optaplanner)












0















I want to do a custom change move to chained planning entities.
When certain conditions are met suppose I want to move E such that it comes after B as in the example below.



A <- B <- C and D <- E <- F become A <- C and D <- B <- E <-F



To achieve this I implemented a CustomChangeMove:



public class CustomChangeMove extends AbstractMove<VehicleRoutingSolution> {

private Customer customer;
private Customer toPreviousStandstill;

private Customer oldTrailingEntity;
private Customer newTrailingEntity;


public CustomChangemove(Customer customer, Customer toPreviousStandstill, Customer oldTrailingEntity, Customer newTrailingEntity) {
this.customer = customer;
this.toPreviousStandstill = toPreviousStandstill;
this.oldTrailingEntity = oldTrailingEntity;
this.newTrailingEntity = newTrailingEntity;
}

@Override
protected void doMoveOnGenuineVariables(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
Standstill oldPreviousStandstill = customer.getPreviousStandstill();

scoreDirector.beforeVariableChanged(customer, "previousStandstill" );
//fix old chain
oldTrailingEntity.setPreviousStandstill(oldPreviousStandstill);
// oldPreviousStandstill.setNextCustomer(oldTrailingEntity); // shadow variables are updated automatically

// move object
customer.setPreviousStandstill(toPreviousStandstill);
// customer.setNextCustomer(newTrailingEntity); shadow variable

//fix new chain
toPreviousStandstill.setNextCustomer(customer);
// toPreviousStandstill.setNextCustomer(null);
// newTrailingEntity.setPreviousStandstill(customer); // shadow variable


scoreDirector.afterVariableChanged(customer, "previousStandstill");
}


@Override
public boolean isMoveDoable(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
return !Objects.equals(customer.getPreviousStandstill(), toPreviousStandstill) ||
!Objects.equals(customer.getNextCustomer(), toPreviousStandstill);
}


I would think that configuring the previousStandstills and nextCustomers this way would fix the chains but instead it is yielding me an IllegalStateException:



java.lang.IllegalStateException: The entity (Customer [shipmentId=xx, vehicle=TimeWindowedVehicle [0]]) has a variable (previousStandstill) with value (Customer [xx, vehicle=TimeWindowedVehicle [0]]) which has a sourceVariableName variable (nextCustomer) with a value (Customer [shipmentId=xxxxx-1, vehicle=TimeWindowedVehicle [0]]) which is not null.
Verify the consistency of your input problem for that sourceVariableName variable.
at org.optaplanner.core.impl.domain.variable.inverserelation.SingletonInverseVariableListener.insert(SingletonInverseVariableListener.java:72) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
at org.optaplanner.core.impl.domain.variable.inverserelation.SingletonInverseVariableListener.afterVariableChanged(SingletonInverseVariableListener.java:51) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
at org.optaplanner.core.impl.domain.variable.listener.support.VariableListenerSupport.triggerVariableListenersInNotificationQueues(VariableListenerSupport.java:209) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
at org.optaplanner.core.impl.score.director.AbstractScoreDirector.triggerVariableListeners(AbstractScoreDirector.java:228) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
at org.optaplanner.core.impl.heuristic.move.AbstractMove.doMove(AbstractMove.java:38) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
at org.optaplanner.core.impl.heuristic.move.AbstractMove.doMove(AbstractMove.java:27) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.doMove(LocalSearchDecider.java:146) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.decideNextStep(LocalSearchDecider.java:120) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
at org.optaplanner.core.impl.localsearch.DefaultLocalSearchPhase.solve(DefaultLocalSearchPhase.java:70) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
at org.optaplanner.core.impl.solver.AbstractSolver.runPhases(AbstractSolver.java:87) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:163) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
at org.optaplanner.benchmark.impl.SubSingleBenchmarkRunner.call(SubSingleBenchmarkRunner.java:106) ~[optaplanner-benchmark-7.0.0.Final.jar:7.0.0.Final]
at org.optaplanner.benchmark.impl.SubSingleBenchmarkRunner.call(SubSingleBenchmarkRunner.java:34) ~[optaplanner-benchmark-7.0.0.Final.jar:7.0.0.Final]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_191]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_191]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_191]
at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_191]


Another idea I have is to use the default ChainedChangeMove:



public class CustomChangeMoveFactory implements MoveListFactory<VehicleRoutingSolution> {

@Override
public List<ChainedChangeMove> createMoveList(VehicleRoutingSolution vrpSol) {
List<ChainedChangeMove> moveList = new ArrayList<>();
List<Customer> customers = vrpSol.getCustomers();
for (Customer c1 : customers) {
for (Customer c2 : customers) {
//if certain condition met add to movelist
moveList.add(new ChainedChangeMove(c1, variableDescriptor ?, c2, c1.getNextCustomer(), c2.getNextCustomer()))
}
}
return moveList;
}

}


But for that I need a variableDescriptor that I don't have.
Any ideas why my CustomChangeMove fails ?



EDIT
After quite some digging I feel that some sort of a circular reference occurs. I find that the method runs fine a few times before throwing an exception and the exception thrown is typically undoing a change that has been done before (due to how I generate these CustomChangeMoves this is not unthinkable).



I find that the exception is always thrown when the change is on the same chain. It occurs when the customer still has a nextCustomer (an @InverseRelationShadowVariable) that is not updated to null which normally it what it normally will be when the previousStandstill is updated.



So, when on the same chain A <- B <- C <- D <- E, the move will be such that to move B behind E: A <- C <- D <- E <- B. This will succeed. The following move will try to put E behind B: A <- C <- D <- B <- E, this is when the exception is thrown. Any ideas on how to deal with this are deeply appreciated.



Also, I don't understand why the move is even executed as isMoveDoable should prevent this from occurring.










share|improve this question





























    0















    I want to do a custom change move to chained planning entities.
    When certain conditions are met suppose I want to move E such that it comes after B as in the example below.



    A <- B <- C and D <- E <- F become A <- C and D <- B <- E <-F



    To achieve this I implemented a CustomChangeMove:



    public class CustomChangeMove extends AbstractMove<VehicleRoutingSolution> {

    private Customer customer;
    private Customer toPreviousStandstill;

    private Customer oldTrailingEntity;
    private Customer newTrailingEntity;


    public CustomChangemove(Customer customer, Customer toPreviousStandstill, Customer oldTrailingEntity, Customer newTrailingEntity) {
    this.customer = customer;
    this.toPreviousStandstill = toPreviousStandstill;
    this.oldTrailingEntity = oldTrailingEntity;
    this.newTrailingEntity = newTrailingEntity;
    }

    @Override
    protected void doMoveOnGenuineVariables(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
    Standstill oldPreviousStandstill = customer.getPreviousStandstill();

    scoreDirector.beforeVariableChanged(customer, "previousStandstill" );
    //fix old chain
    oldTrailingEntity.setPreviousStandstill(oldPreviousStandstill);
    // oldPreviousStandstill.setNextCustomer(oldTrailingEntity); // shadow variables are updated automatically

    // move object
    customer.setPreviousStandstill(toPreviousStandstill);
    // customer.setNextCustomer(newTrailingEntity); shadow variable

    //fix new chain
    toPreviousStandstill.setNextCustomer(customer);
    // toPreviousStandstill.setNextCustomer(null);
    // newTrailingEntity.setPreviousStandstill(customer); // shadow variable


    scoreDirector.afterVariableChanged(customer, "previousStandstill");
    }


    @Override
    public boolean isMoveDoable(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
    return !Objects.equals(customer.getPreviousStandstill(), toPreviousStandstill) ||
    !Objects.equals(customer.getNextCustomer(), toPreviousStandstill);
    }


    I would think that configuring the previousStandstills and nextCustomers this way would fix the chains but instead it is yielding me an IllegalStateException:



    java.lang.IllegalStateException: The entity (Customer [shipmentId=xx, vehicle=TimeWindowedVehicle [0]]) has a variable (previousStandstill) with value (Customer [xx, vehicle=TimeWindowedVehicle [0]]) which has a sourceVariableName variable (nextCustomer) with a value (Customer [shipmentId=xxxxx-1, vehicle=TimeWindowedVehicle [0]]) which is not null.
    Verify the consistency of your input problem for that sourceVariableName variable.
    at org.optaplanner.core.impl.domain.variable.inverserelation.SingletonInverseVariableListener.insert(SingletonInverseVariableListener.java:72) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.domain.variable.inverserelation.SingletonInverseVariableListener.afterVariableChanged(SingletonInverseVariableListener.java:51) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.domain.variable.listener.support.VariableListenerSupport.triggerVariableListenersInNotificationQueues(VariableListenerSupport.java:209) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.score.director.AbstractScoreDirector.triggerVariableListeners(AbstractScoreDirector.java:228) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.heuristic.move.AbstractMove.doMove(AbstractMove.java:38) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.heuristic.move.AbstractMove.doMove(AbstractMove.java:27) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.doMove(LocalSearchDecider.java:146) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.decideNextStep(LocalSearchDecider.java:120) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.localsearch.DefaultLocalSearchPhase.solve(DefaultLocalSearchPhase.java:70) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.solver.AbstractSolver.runPhases(AbstractSolver.java:87) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:163) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.benchmark.impl.SubSingleBenchmarkRunner.call(SubSingleBenchmarkRunner.java:106) ~[optaplanner-benchmark-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.benchmark.impl.SubSingleBenchmarkRunner.call(SubSingleBenchmarkRunner.java:34) ~[optaplanner-benchmark-7.0.0.Final.jar:7.0.0.Final]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_191]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_191]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_191]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_191]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_191]
    at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_191]


    Another idea I have is to use the default ChainedChangeMove:



    public class CustomChangeMoveFactory implements MoveListFactory<VehicleRoutingSolution> {

    @Override
    public List<ChainedChangeMove> createMoveList(VehicleRoutingSolution vrpSol) {
    List<ChainedChangeMove> moveList = new ArrayList<>();
    List<Customer> customers = vrpSol.getCustomers();
    for (Customer c1 : customers) {
    for (Customer c2 : customers) {
    //if certain condition met add to movelist
    moveList.add(new ChainedChangeMove(c1, variableDescriptor ?, c2, c1.getNextCustomer(), c2.getNextCustomer()))
    }
    }
    return moveList;
    }

    }


    But for that I need a variableDescriptor that I don't have.
    Any ideas why my CustomChangeMove fails ?



    EDIT
    After quite some digging I feel that some sort of a circular reference occurs. I find that the method runs fine a few times before throwing an exception and the exception thrown is typically undoing a change that has been done before (due to how I generate these CustomChangeMoves this is not unthinkable).



    I find that the exception is always thrown when the change is on the same chain. It occurs when the customer still has a nextCustomer (an @InverseRelationShadowVariable) that is not updated to null which normally it what it normally will be when the previousStandstill is updated.



    So, when on the same chain A <- B <- C <- D <- E, the move will be such that to move B behind E: A <- C <- D <- E <- B. This will succeed. The following move will try to put E behind B: A <- C <- D <- B <- E, this is when the exception is thrown. Any ideas on how to deal with this are deeply appreciated.



    Also, I don't understand why the move is even executed as isMoveDoable should prevent this from occurring.










    share|improve this question



























      0












      0








      0


      1






      I want to do a custom change move to chained planning entities.
      When certain conditions are met suppose I want to move E such that it comes after B as in the example below.



      A <- B <- C and D <- E <- F become A <- C and D <- B <- E <-F



      To achieve this I implemented a CustomChangeMove:



      public class CustomChangeMove extends AbstractMove<VehicleRoutingSolution> {

      private Customer customer;
      private Customer toPreviousStandstill;

      private Customer oldTrailingEntity;
      private Customer newTrailingEntity;


      public CustomChangemove(Customer customer, Customer toPreviousStandstill, Customer oldTrailingEntity, Customer newTrailingEntity) {
      this.customer = customer;
      this.toPreviousStandstill = toPreviousStandstill;
      this.oldTrailingEntity = oldTrailingEntity;
      this.newTrailingEntity = newTrailingEntity;
      }

      @Override
      protected void doMoveOnGenuineVariables(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
      Standstill oldPreviousStandstill = customer.getPreviousStandstill();

      scoreDirector.beforeVariableChanged(customer, "previousStandstill" );
      //fix old chain
      oldTrailingEntity.setPreviousStandstill(oldPreviousStandstill);
      // oldPreviousStandstill.setNextCustomer(oldTrailingEntity); // shadow variables are updated automatically

      // move object
      customer.setPreviousStandstill(toPreviousStandstill);
      // customer.setNextCustomer(newTrailingEntity); shadow variable

      //fix new chain
      toPreviousStandstill.setNextCustomer(customer);
      // toPreviousStandstill.setNextCustomer(null);
      // newTrailingEntity.setPreviousStandstill(customer); // shadow variable


      scoreDirector.afterVariableChanged(customer, "previousStandstill");
      }


      @Override
      public boolean isMoveDoable(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
      return !Objects.equals(customer.getPreviousStandstill(), toPreviousStandstill) ||
      !Objects.equals(customer.getNextCustomer(), toPreviousStandstill);
      }


      I would think that configuring the previousStandstills and nextCustomers this way would fix the chains but instead it is yielding me an IllegalStateException:



      java.lang.IllegalStateException: The entity (Customer [shipmentId=xx, vehicle=TimeWindowedVehicle [0]]) has a variable (previousStandstill) with value (Customer [xx, vehicle=TimeWindowedVehicle [0]]) which has a sourceVariableName variable (nextCustomer) with a value (Customer [shipmentId=xxxxx-1, vehicle=TimeWindowedVehicle [0]]) which is not null.
      Verify the consistency of your input problem for that sourceVariableName variable.
      at org.optaplanner.core.impl.domain.variable.inverserelation.SingletonInverseVariableListener.insert(SingletonInverseVariableListener.java:72) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.domain.variable.inverserelation.SingletonInverseVariableListener.afterVariableChanged(SingletonInverseVariableListener.java:51) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.domain.variable.listener.support.VariableListenerSupport.triggerVariableListenersInNotificationQueues(VariableListenerSupport.java:209) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.score.director.AbstractScoreDirector.triggerVariableListeners(AbstractScoreDirector.java:228) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.heuristic.move.AbstractMove.doMove(AbstractMove.java:38) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.heuristic.move.AbstractMove.doMove(AbstractMove.java:27) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.doMove(LocalSearchDecider.java:146) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.decideNextStep(LocalSearchDecider.java:120) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.localsearch.DefaultLocalSearchPhase.solve(DefaultLocalSearchPhase.java:70) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.solver.AbstractSolver.runPhases(AbstractSolver.java:87) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:163) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.benchmark.impl.SubSingleBenchmarkRunner.call(SubSingleBenchmarkRunner.java:106) ~[optaplanner-benchmark-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.benchmark.impl.SubSingleBenchmarkRunner.call(SubSingleBenchmarkRunner.java:34) ~[optaplanner-benchmark-7.0.0.Final.jar:7.0.0.Final]
      at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_191]
      at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_191]
      at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_191]
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_191]
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_191]
      at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_191]


      Another idea I have is to use the default ChainedChangeMove:



      public class CustomChangeMoveFactory implements MoveListFactory<VehicleRoutingSolution> {

      @Override
      public List<ChainedChangeMove> createMoveList(VehicleRoutingSolution vrpSol) {
      List<ChainedChangeMove> moveList = new ArrayList<>();
      List<Customer> customers = vrpSol.getCustomers();
      for (Customer c1 : customers) {
      for (Customer c2 : customers) {
      //if certain condition met add to movelist
      moveList.add(new ChainedChangeMove(c1, variableDescriptor ?, c2, c1.getNextCustomer(), c2.getNextCustomer()))
      }
      }
      return moveList;
      }

      }


      But for that I need a variableDescriptor that I don't have.
      Any ideas why my CustomChangeMove fails ?



      EDIT
      After quite some digging I feel that some sort of a circular reference occurs. I find that the method runs fine a few times before throwing an exception and the exception thrown is typically undoing a change that has been done before (due to how I generate these CustomChangeMoves this is not unthinkable).



      I find that the exception is always thrown when the change is on the same chain. It occurs when the customer still has a nextCustomer (an @InverseRelationShadowVariable) that is not updated to null which normally it what it normally will be when the previousStandstill is updated.



      So, when on the same chain A <- B <- C <- D <- E, the move will be such that to move B behind E: A <- C <- D <- E <- B. This will succeed. The following move will try to put E behind B: A <- C <- D <- B <- E, this is when the exception is thrown. Any ideas on how to deal with this are deeply appreciated.



      Also, I don't understand why the move is even executed as isMoveDoable should prevent this from occurring.










      share|improve this question
















      I want to do a custom change move to chained planning entities.
      When certain conditions are met suppose I want to move E such that it comes after B as in the example below.



      A <- B <- C and D <- E <- F become A <- C and D <- B <- E <-F



      To achieve this I implemented a CustomChangeMove:



      public class CustomChangeMove extends AbstractMove<VehicleRoutingSolution> {

      private Customer customer;
      private Customer toPreviousStandstill;

      private Customer oldTrailingEntity;
      private Customer newTrailingEntity;


      public CustomChangemove(Customer customer, Customer toPreviousStandstill, Customer oldTrailingEntity, Customer newTrailingEntity) {
      this.customer = customer;
      this.toPreviousStandstill = toPreviousStandstill;
      this.oldTrailingEntity = oldTrailingEntity;
      this.newTrailingEntity = newTrailingEntity;
      }

      @Override
      protected void doMoveOnGenuineVariables(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
      Standstill oldPreviousStandstill = customer.getPreviousStandstill();

      scoreDirector.beforeVariableChanged(customer, "previousStandstill" );
      //fix old chain
      oldTrailingEntity.setPreviousStandstill(oldPreviousStandstill);
      // oldPreviousStandstill.setNextCustomer(oldTrailingEntity); // shadow variables are updated automatically

      // move object
      customer.setPreviousStandstill(toPreviousStandstill);
      // customer.setNextCustomer(newTrailingEntity); shadow variable

      //fix new chain
      toPreviousStandstill.setNextCustomer(customer);
      // toPreviousStandstill.setNextCustomer(null);
      // newTrailingEntity.setPreviousStandstill(customer); // shadow variable


      scoreDirector.afterVariableChanged(customer, "previousStandstill");
      }


      @Override
      public boolean isMoveDoable(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
      return !Objects.equals(customer.getPreviousStandstill(), toPreviousStandstill) ||
      !Objects.equals(customer.getNextCustomer(), toPreviousStandstill);
      }


      I would think that configuring the previousStandstills and nextCustomers this way would fix the chains but instead it is yielding me an IllegalStateException:



      java.lang.IllegalStateException: The entity (Customer [shipmentId=xx, vehicle=TimeWindowedVehicle [0]]) has a variable (previousStandstill) with value (Customer [xx, vehicle=TimeWindowedVehicle [0]]) which has a sourceVariableName variable (nextCustomer) with a value (Customer [shipmentId=xxxxx-1, vehicle=TimeWindowedVehicle [0]]) which is not null.
      Verify the consistency of your input problem for that sourceVariableName variable.
      at org.optaplanner.core.impl.domain.variable.inverserelation.SingletonInverseVariableListener.insert(SingletonInverseVariableListener.java:72) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.domain.variable.inverserelation.SingletonInverseVariableListener.afterVariableChanged(SingletonInverseVariableListener.java:51) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.domain.variable.listener.support.VariableListenerSupport.triggerVariableListenersInNotificationQueues(VariableListenerSupport.java:209) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.score.director.AbstractScoreDirector.triggerVariableListeners(AbstractScoreDirector.java:228) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.heuristic.move.AbstractMove.doMove(AbstractMove.java:38) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.heuristic.move.AbstractMove.doMove(AbstractMove.java:27) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.doMove(LocalSearchDecider.java:146) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.decideNextStep(LocalSearchDecider.java:120) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.localsearch.DefaultLocalSearchPhase.solve(DefaultLocalSearchPhase.java:70) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.solver.AbstractSolver.runPhases(AbstractSolver.java:87) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:163) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.benchmark.impl.SubSingleBenchmarkRunner.call(SubSingleBenchmarkRunner.java:106) ~[optaplanner-benchmark-7.0.0.Final.jar:7.0.0.Final]
      at org.optaplanner.benchmark.impl.SubSingleBenchmarkRunner.call(SubSingleBenchmarkRunner.java:34) ~[optaplanner-benchmark-7.0.0.Final.jar:7.0.0.Final]
      at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_191]
      at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_191]
      at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_191]
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_191]
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_191]
      at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_191]


      Another idea I have is to use the default ChainedChangeMove:



      public class CustomChangeMoveFactory implements MoveListFactory<VehicleRoutingSolution> {

      @Override
      public List<ChainedChangeMove> createMoveList(VehicleRoutingSolution vrpSol) {
      List<ChainedChangeMove> moveList = new ArrayList<>();
      List<Customer> customers = vrpSol.getCustomers();
      for (Customer c1 : customers) {
      for (Customer c2 : customers) {
      //if certain condition met add to movelist
      moveList.add(new ChainedChangeMove(c1, variableDescriptor ?, c2, c1.getNextCustomer(), c2.getNextCustomer()))
      }
      }
      return moveList;
      }

      }


      But for that I need a variableDescriptor that I don't have.
      Any ideas why my CustomChangeMove fails ?



      EDIT
      After quite some digging I feel that some sort of a circular reference occurs. I find that the method runs fine a few times before throwing an exception and the exception thrown is typically undoing a change that has been done before (due to how I generate these CustomChangeMoves this is not unthinkable).



      I find that the exception is always thrown when the change is on the same chain. It occurs when the customer still has a nextCustomer (an @InverseRelationShadowVariable) that is not updated to null which normally it what it normally will be when the previousStandstill is updated.



      So, when on the same chain A <- B <- C <- D <- E, the move will be such that to move B behind E: A <- C <- D <- E <- B. This will succeed. The following move will try to put E behind B: A <- C <- D <- B <- E, this is when the exception is thrown. Any ideas on how to deal with this are deeply appreciated.



      Also, I don't understand why the move is even executed as isMoveDoable should prevent this from occurring.







      java optaplanner






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Jan 2 at 3:07







      k88

















      asked Dec 26 '18 at 7:32









      k88k88

      399315




      399315
























          1 Answer
          1






          active

          oldest

          votes


















          0














          I figured it out I think. So far my implementation seems to work good and the issue seems to be occurring in createUndoMove code that I had not posted and which was wrong.



          This full code seems to work (at least there are no exceptions being thrown):



          public class CustomChangeMove extends AbstractMove<VehicleRoutingSolution> {

          private Customer customer;
          private Customer toPreviousStandstill;

          private Customer oldTrailingEntity;
          private Customer newTrailingEntity;


          public CustomChangemove(Customer customer, Customer toPreviousStandstill, Customer oldTrailingEntity, Customer newTrailingEntity) {
          this.customer = customer;
          this.toPreviousStandstill = toPreviousStandstill;
          this.oldTrailingEntity = oldTrailingEntity;
          this.newTrailingEntity = newTrailingEntity;
          }

          @Override
          protected void doMoveOnGenuineVariables(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
          Standstill oldPreviousStandstill = customer.getPreviousStandstill();

          // move object
          scoreDirector.beforeVariableChanged(customer, "previousStandstill" );
          customer.setPreviousStandstill(toPreviousStandstill);
          scoreDirector.afterVariableChanged(customer, "previousStandstill");

          //fix old chain
          if (oldTrailingEntity != null) {
          scoreDirector.beforeVariableChanged(oldTrailingEntity, "previousStandstill" );
          oldTrailingEntity.setPreviousStandstill(oldPreviousStandstill);
          scoreDirector.afterVariableChanged(oldTrailingEntity, "previousStandstill");
          }

          //fix new chain
          if (newTrailingEntity != null) {
          scoreDirector.beforeVariableChanged(newTrailingEntity, "previousStandstill" );
          newTrailingEntity.setPreviousStandstill(customer);
          scoreDirector.afterVariableChanged(newTrailingEntity, "previousStandstill");
          }
          }


          @Override
          public boolean isMoveDoable(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
          return !Objects.equals(customer.getPreviousStandstill(), toPreviousStandstill) ||
          !Objects.equals(customer.getNextCustomer(), toPreviousStandstill);
          }

          @Override
          protected AbstractMove<VehicleRoutingSolution> createUndoMove(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
          return new MultidropChangeMove(customer, customer.getPreviousStandstill(), newTrailingEntity, oldTrailingEntity);
          }

          @Override
          public Collection<?> getPlanningEntities() {
          // return Arrays.asList(customer, newTrailingEntity, oldTrailingEntity);
          return Collections.singletonList(customer);
          }

          @Override
          public Collection<?> getPlanningValues() {
          // return Arrays.asList(customer.getPreviousStandstill(), newTrailingEntity.getPreviousStandstill(), oldTrailingEntity.getPreviousStandstill());
          return Collections.singleton(customer.getPreviousStandstill());
          }


          EDIT
          This answer was also not right, the problem was solved coincidentally because it occured in some datasets while others were exempt.



          The solution seems to be to not store the changing entities at construction time of the move as these are subjected to change later.



          So a constructor of



          public CustomChangemove(Customer customer, Customer toPreviousStandstill) {
          this.customer = customer;
          this.toPreviousStandstill = toPreviousStandstill;
          }


          is much better, oldTrailingEntity and newTrailingEntity can then be retrieved in doMoveOnGenuinineVariables as customer.getNextCustomer() or toPreviousStandstill.getNextCustomer(). This ensures the correct entities are retrieved and not some entity that has been changed.






          share|improve this answer

























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53928884%2fcustomchangemove-in-chained-planning-entities-throws-illegalstateexception-opta%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            I figured it out I think. So far my implementation seems to work good and the issue seems to be occurring in createUndoMove code that I had not posted and which was wrong.



            This full code seems to work (at least there are no exceptions being thrown):



            public class CustomChangeMove extends AbstractMove<VehicleRoutingSolution> {

            private Customer customer;
            private Customer toPreviousStandstill;

            private Customer oldTrailingEntity;
            private Customer newTrailingEntity;


            public CustomChangemove(Customer customer, Customer toPreviousStandstill, Customer oldTrailingEntity, Customer newTrailingEntity) {
            this.customer = customer;
            this.toPreviousStandstill = toPreviousStandstill;
            this.oldTrailingEntity = oldTrailingEntity;
            this.newTrailingEntity = newTrailingEntity;
            }

            @Override
            protected void doMoveOnGenuineVariables(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
            Standstill oldPreviousStandstill = customer.getPreviousStandstill();

            // move object
            scoreDirector.beforeVariableChanged(customer, "previousStandstill" );
            customer.setPreviousStandstill(toPreviousStandstill);
            scoreDirector.afterVariableChanged(customer, "previousStandstill");

            //fix old chain
            if (oldTrailingEntity != null) {
            scoreDirector.beforeVariableChanged(oldTrailingEntity, "previousStandstill" );
            oldTrailingEntity.setPreviousStandstill(oldPreviousStandstill);
            scoreDirector.afterVariableChanged(oldTrailingEntity, "previousStandstill");
            }

            //fix new chain
            if (newTrailingEntity != null) {
            scoreDirector.beforeVariableChanged(newTrailingEntity, "previousStandstill" );
            newTrailingEntity.setPreviousStandstill(customer);
            scoreDirector.afterVariableChanged(newTrailingEntity, "previousStandstill");
            }
            }


            @Override
            public boolean isMoveDoable(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
            return !Objects.equals(customer.getPreviousStandstill(), toPreviousStandstill) ||
            !Objects.equals(customer.getNextCustomer(), toPreviousStandstill);
            }

            @Override
            protected AbstractMove<VehicleRoutingSolution> createUndoMove(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
            return new MultidropChangeMove(customer, customer.getPreviousStandstill(), newTrailingEntity, oldTrailingEntity);
            }

            @Override
            public Collection<?> getPlanningEntities() {
            // return Arrays.asList(customer, newTrailingEntity, oldTrailingEntity);
            return Collections.singletonList(customer);
            }

            @Override
            public Collection<?> getPlanningValues() {
            // return Arrays.asList(customer.getPreviousStandstill(), newTrailingEntity.getPreviousStandstill(), oldTrailingEntity.getPreviousStandstill());
            return Collections.singleton(customer.getPreviousStandstill());
            }


            EDIT
            This answer was also not right, the problem was solved coincidentally because it occured in some datasets while others were exempt.



            The solution seems to be to not store the changing entities at construction time of the move as these are subjected to change later.



            So a constructor of



            public CustomChangemove(Customer customer, Customer toPreviousStandstill) {
            this.customer = customer;
            this.toPreviousStandstill = toPreviousStandstill;
            }


            is much better, oldTrailingEntity and newTrailingEntity can then be retrieved in doMoveOnGenuinineVariables as customer.getNextCustomer() or toPreviousStandstill.getNextCustomer(). This ensures the correct entities are retrieved and not some entity that has been changed.






            share|improve this answer






























              0














              I figured it out I think. So far my implementation seems to work good and the issue seems to be occurring in createUndoMove code that I had not posted and which was wrong.



              This full code seems to work (at least there are no exceptions being thrown):



              public class CustomChangeMove extends AbstractMove<VehicleRoutingSolution> {

              private Customer customer;
              private Customer toPreviousStandstill;

              private Customer oldTrailingEntity;
              private Customer newTrailingEntity;


              public CustomChangemove(Customer customer, Customer toPreviousStandstill, Customer oldTrailingEntity, Customer newTrailingEntity) {
              this.customer = customer;
              this.toPreviousStandstill = toPreviousStandstill;
              this.oldTrailingEntity = oldTrailingEntity;
              this.newTrailingEntity = newTrailingEntity;
              }

              @Override
              protected void doMoveOnGenuineVariables(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
              Standstill oldPreviousStandstill = customer.getPreviousStandstill();

              // move object
              scoreDirector.beforeVariableChanged(customer, "previousStandstill" );
              customer.setPreviousStandstill(toPreviousStandstill);
              scoreDirector.afterVariableChanged(customer, "previousStandstill");

              //fix old chain
              if (oldTrailingEntity != null) {
              scoreDirector.beforeVariableChanged(oldTrailingEntity, "previousStandstill" );
              oldTrailingEntity.setPreviousStandstill(oldPreviousStandstill);
              scoreDirector.afterVariableChanged(oldTrailingEntity, "previousStandstill");
              }

              //fix new chain
              if (newTrailingEntity != null) {
              scoreDirector.beforeVariableChanged(newTrailingEntity, "previousStandstill" );
              newTrailingEntity.setPreviousStandstill(customer);
              scoreDirector.afterVariableChanged(newTrailingEntity, "previousStandstill");
              }
              }


              @Override
              public boolean isMoveDoable(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
              return !Objects.equals(customer.getPreviousStandstill(), toPreviousStandstill) ||
              !Objects.equals(customer.getNextCustomer(), toPreviousStandstill);
              }

              @Override
              protected AbstractMove<VehicleRoutingSolution> createUndoMove(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
              return new MultidropChangeMove(customer, customer.getPreviousStandstill(), newTrailingEntity, oldTrailingEntity);
              }

              @Override
              public Collection<?> getPlanningEntities() {
              // return Arrays.asList(customer, newTrailingEntity, oldTrailingEntity);
              return Collections.singletonList(customer);
              }

              @Override
              public Collection<?> getPlanningValues() {
              // return Arrays.asList(customer.getPreviousStandstill(), newTrailingEntity.getPreviousStandstill(), oldTrailingEntity.getPreviousStandstill());
              return Collections.singleton(customer.getPreviousStandstill());
              }


              EDIT
              This answer was also not right, the problem was solved coincidentally because it occured in some datasets while others were exempt.



              The solution seems to be to not store the changing entities at construction time of the move as these are subjected to change later.



              So a constructor of



              public CustomChangemove(Customer customer, Customer toPreviousStandstill) {
              this.customer = customer;
              this.toPreviousStandstill = toPreviousStandstill;
              }


              is much better, oldTrailingEntity and newTrailingEntity can then be retrieved in doMoveOnGenuinineVariables as customer.getNextCustomer() or toPreviousStandstill.getNextCustomer(). This ensures the correct entities are retrieved and not some entity that has been changed.






              share|improve this answer




























                0












                0








                0







                I figured it out I think. So far my implementation seems to work good and the issue seems to be occurring in createUndoMove code that I had not posted and which was wrong.



                This full code seems to work (at least there are no exceptions being thrown):



                public class CustomChangeMove extends AbstractMove<VehicleRoutingSolution> {

                private Customer customer;
                private Customer toPreviousStandstill;

                private Customer oldTrailingEntity;
                private Customer newTrailingEntity;


                public CustomChangemove(Customer customer, Customer toPreviousStandstill, Customer oldTrailingEntity, Customer newTrailingEntity) {
                this.customer = customer;
                this.toPreviousStandstill = toPreviousStandstill;
                this.oldTrailingEntity = oldTrailingEntity;
                this.newTrailingEntity = newTrailingEntity;
                }

                @Override
                protected void doMoveOnGenuineVariables(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
                Standstill oldPreviousStandstill = customer.getPreviousStandstill();

                // move object
                scoreDirector.beforeVariableChanged(customer, "previousStandstill" );
                customer.setPreviousStandstill(toPreviousStandstill);
                scoreDirector.afterVariableChanged(customer, "previousStandstill");

                //fix old chain
                if (oldTrailingEntity != null) {
                scoreDirector.beforeVariableChanged(oldTrailingEntity, "previousStandstill" );
                oldTrailingEntity.setPreviousStandstill(oldPreviousStandstill);
                scoreDirector.afterVariableChanged(oldTrailingEntity, "previousStandstill");
                }

                //fix new chain
                if (newTrailingEntity != null) {
                scoreDirector.beforeVariableChanged(newTrailingEntity, "previousStandstill" );
                newTrailingEntity.setPreviousStandstill(customer);
                scoreDirector.afterVariableChanged(newTrailingEntity, "previousStandstill");
                }
                }


                @Override
                public boolean isMoveDoable(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
                return !Objects.equals(customer.getPreviousStandstill(), toPreviousStandstill) ||
                !Objects.equals(customer.getNextCustomer(), toPreviousStandstill);
                }

                @Override
                protected AbstractMove<VehicleRoutingSolution> createUndoMove(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
                return new MultidropChangeMove(customer, customer.getPreviousStandstill(), newTrailingEntity, oldTrailingEntity);
                }

                @Override
                public Collection<?> getPlanningEntities() {
                // return Arrays.asList(customer, newTrailingEntity, oldTrailingEntity);
                return Collections.singletonList(customer);
                }

                @Override
                public Collection<?> getPlanningValues() {
                // return Arrays.asList(customer.getPreviousStandstill(), newTrailingEntity.getPreviousStandstill(), oldTrailingEntity.getPreviousStandstill());
                return Collections.singleton(customer.getPreviousStandstill());
                }


                EDIT
                This answer was also not right, the problem was solved coincidentally because it occured in some datasets while others were exempt.



                The solution seems to be to not store the changing entities at construction time of the move as these are subjected to change later.



                So a constructor of



                public CustomChangemove(Customer customer, Customer toPreviousStandstill) {
                this.customer = customer;
                this.toPreviousStandstill = toPreviousStandstill;
                }


                is much better, oldTrailingEntity and newTrailingEntity can then be retrieved in doMoveOnGenuinineVariables as customer.getNextCustomer() or toPreviousStandstill.getNextCustomer(). This ensures the correct entities are retrieved and not some entity that has been changed.






                share|improve this answer















                I figured it out I think. So far my implementation seems to work good and the issue seems to be occurring in createUndoMove code that I had not posted and which was wrong.



                This full code seems to work (at least there are no exceptions being thrown):



                public class CustomChangeMove extends AbstractMove<VehicleRoutingSolution> {

                private Customer customer;
                private Customer toPreviousStandstill;

                private Customer oldTrailingEntity;
                private Customer newTrailingEntity;


                public CustomChangemove(Customer customer, Customer toPreviousStandstill, Customer oldTrailingEntity, Customer newTrailingEntity) {
                this.customer = customer;
                this.toPreviousStandstill = toPreviousStandstill;
                this.oldTrailingEntity = oldTrailingEntity;
                this.newTrailingEntity = newTrailingEntity;
                }

                @Override
                protected void doMoveOnGenuineVariables(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
                Standstill oldPreviousStandstill = customer.getPreviousStandstill();

                // move object
                scoreDirector.beforeVariableChanged(customer, "previousStandstill" );
                customer.setPreviousStandstill(toPreviousStandstill);
                scoreDirector.afterVariableChanged(customer, "previousStandstill");

                //fix old chain
                if (oldTrailingEntity != null) {
                scoreDirector.beforeVariableChanged(oldTrailingEntity, "previousStandstill" );
                oldTrailingEntity.setPreviousStandstill(oldPreviousStandstill);
                scoreDirector.afterVariableChanged(oldTrailingEntity, "previousStandstill");
                }

                //fix new chain
                if (newTrailingEntity != null) {
                scoreDirector.beforeVariableChanged(newTrailingEntity, "previousStandstill" );
                newTrailingEntity.setPreviousStandstill(customer);
                scoreDirector.afterVariableChanged(newTrailingEntity, "previousStandstill");
                }
                }


                @Override
                public boolean isMoveDoable(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
                return !Objects.equals(customer.getPreviousStandstill(), toPreviousStandstill) ||
                !Objects.equals(customer.getNextCustomer(), toPreviousStandstill);
                }

                @Override
                protected AbstractMove<VehicleRoutingSolution> createUndoMove(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
                return new MultidropChangeMove(customer, customer.getPreviousStandstill(), newTrailingEntity, oldTrailingEntity);
                }

                @Override
                public Collection<?> getPlanningEntities() {
                // return Arrays.asList(customer, newTrailingEntity, oldTrailingEntity);
                return Collections.singletonList(customer);
                }

                @Override
                public Collection<?> getPlanningValues() {
                // return Arrays.asList(customer.getPreviousStandstill(), newTrailingEntity.getPreviousStandstill(), oldTrailingEntity.getPreviousStandstill());
                return Collections.singleton(customer.getPreviousStandstill());
                }


                EDIT
                This answer was also not right, the problem was solved coincidentally because it occured in some datasets while others were exempt.



                The solution seems to be to not store the changing entities at construction time of the move as these are subjected to change later.



                So a constructor of



                public CustomChangemove(Customer customer, Customer toPreviousStandstill) {
                this.customer = customer;
                this.toPreviousStandstill = toPreviousStandstill;
                }


                is much better, oldTrailingEntity and newTrailingEntity can then be retrieved in doMoveOnGenuinineVariables as customer.getNextCustomer() or toPreviousStandstill.getNextCustomer(). This ensures the correct entities are retrieved and not some entity that has been changed.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Jan 9 at 1:05

























                answered Jan 2 at 4:51









                k88k88

                399315




                399315
































                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53928884%2fcustomchangemove-in-chained-planning-entities-throws-illegalstateexception-opta%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    MongoDB - Not Authorized To Execute Command

                    How to fix TextFormField cause rebuild widget in Flutter

                    in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith