Optaplanner solutionClass entityCollectionProperty should never return null error when simple JSON object passed to controller

1 answer

I am working off of the optaplanner-spring-boot-starter cloud-balancing project and I am trying to assign shifts to employees based on their skill level. However when I pass a JSON object to my roster controller I get an error saying:

java.lang.IllegalArgumentException: The solutionClass (class com.redhat.optaplannersbs.domain.Roster)'s entityCollectionProperty (bean property shiftList on class com.redhat.optaplannersbs.domain.Roster) should never return null.

I do not understand what the problem is as I am basically doing the same thing as the cloud-balancing problem and that runs and solves fine.

Here is my code for the employee class:

public class Employee {     private int eid;      private String name;      private int skillLevel;      public Employee(){      }      public int getEid() {         return eid;     }      public void setEid(int eid) {         this.eid = eid;     }      public String getName() {         return name;     }      public void setName(String name) {         this.name = name;     }      // constraint getters and setters     public int getSkillLevel() {         return skillLevel;     }      public void setSkillLevel(int skillLevel) {         this.skillLevel = skillLevel;     }   } 

Here is my code for the shift class:

 @PlanningEntity public class Shift {     private int sid;      private LocalTime startTime;      private LocalTime endTime;      private int requiredSkillLevel;      @PlanningVariable(valueRangeProviderRefs = "employee")     private Employee employee;      public Shift(){      }      public Shift(Long deptId, Long spotId, LocalTime startTime,                  LocalTime endTime, Long employeeId){         this.startTime = startTime;         this.endTime = endTime;     }      public int getSid() {         return sid;     }      public void setSid(int sid) {         this.sid = sid;     }      public LocalTime getStartTime() {         return startTime;     }      public void setStartTime(LocalTime startTime) {         this.startTime = startTime;     }      public LocalTime getEndTime() {         return endTime;     }      public void setEndTime(LocalTime endTime) {         this.endTime = endTime;     }       // planning variable getter and setter      public Employee getEmployee() {         return employee;     }      public void setEmployee(Employee employee) {         this.employee = employee;     }      public int getRequiredSkillLevel() {         return requiredSkillLevel;     }      public void setRequiredSkillLevel(int requiredSkillLevel) {         this.requiredSkillLevel = requiredSkillLevel;     } } 

Here is my Roster class:

 @PlanningSolution public class Roster {      private List<Employee> employeeList;      private List<Shift> shiftList;      private HardSoftScore score;      public Roster(List<Employee> employeeList, List<Shift> shiftList) {         this.employeeList = employeeList;         this.shiftList = shiftList;     }      @ProblemFactCollectionProperty     @ValueRangeProvider(id="employee")     public List<Employee> getEmployeeList() {         return employeeList;     }      public void setEmployeeList(List<Employee> employeeList) {         this.employeeList = employeeList;     }      @PlanningEntityCollectionProperty     public List<Shift> getShiftList() {         return shiftList;     }      public void setShiftList(List<Shift> shiftList) {         this.shiftList = shiftList;     }      @PlanningScore     public HardSoftScore getScore() {         return score;     }      public void setScore(HardSoftScore score) {         this.score = score;     } } 

Here is my constraint provider:

    public class ConstraintProvider implements org.optaplanner.core.api.score.stream.ConstraintProvider {     @Override     public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {         return new Constraint[]{                 requiredSkillLevelOfEmployeesForShifts(constraintFactory)         };     }      private Constraint requiredSkillLevelOfEmployeesForShifts(ConstraintFactory constraintFactory) {         return constraintFactory.from(Shift.class)                 .groupBy(Shift::getEmployee, sum(Shift::getRequiredSkillLevel))                 .filter((employee, requiredSkillLevel) -> requiredSkillLevel > employee.getSkillLevel())                 .penalize("requiredSkillLevelForShifts",                 HardSoftScore.ONE_HARD,                 (employee, requiredSkillLevel) -> requiredSkillLevel - employee.getSkillLevel());     } } 

Here is my controller:

@RestController @RequestMapping("/roster") public class RosterController {      @Autowired     private SolverManager<Roster, UUID> solverManager;      @PostMapping("/solve")     public Roster solve(@RequestBody Roster problem) {         UUID problemId = UUID.randomUUID();         // Submit the problem to start solving         SolverJob<Roster, UUID> solverJob = solverManager.solve(problemId, problem);         Roster solution;         try {             // Wait until the solving ends             solution = solverJob.getFinalBestSolution();         } catch (InterruptedException | ExecutionException e) {             throw new IllegalStateException("Solving failed.", e);         }         return solution;     }  } 

The JSON data I pass via a post request is as follows:

 {    "shifts":[       {          "sid":0,          "startTime":"09:00",          "endTime":"18:00",          "requiredSkillLevel": 12       },       {          "sid":1,          "startTime":"12:00",          "endTime":"20:00",          "requiredSkillLevel": 10       },       {          "sid":2,          "startTime":"18:00",          "endTime":"00:00",          "requiredSkillLevel": 10       },       {          "sid": 3,          "startTime":"09:00",          "endTime":"18:00",          "requiredSkillLevel": 12       },       {          "sid":4,          "startTime":"12:00",          "endTime":"20:00",          "requiredSkillLevel": 10       },       {          "sid":5,          "startTime":"18:00",          "endTime":"00:00",          "requiredSkillLevel":10       },       {          "sid":6,          "startTime":"09:00",          "endTime":"18:00",          "requiredSkillLevel": 12       },       {          "sid":7,          "startTime":"12:00",          "endTime":"20:00",          "requiredSkillLevel": 10       },       {          "sid":8,          "startTime":"18:00",          "endTime":"00:00",          "requiredSkillLevel":10       },       {          "sid":9,          "startTime":"09:00",          "endTime":"18:00",          "requiredSkillLevel": 12       },       {          "sid":10,          "startTime":"12:00",          "endTime":"20:00",          "requiredSkillLevel": 10       },       {          "sid":11,          "startTime":"18:00",          "endTime":"00:00",          "requiredSkillLevel":10       },       {          "sid":12,          "startTime":"09:00",          "endTime":"18:00",          "requiredSkillLevel": 12       },       {          "sid":13,          "startTime":"12:00",          "endTime":"20:00",          "requiredSkillLevel": 10       },       {          "sid":14,          "startTime":"18:00",          "endTime":"00:00",          "requiredSkillLevel":10       }    ],    "employees":[       {          "eid":0,          "name":"john",          "skillLevel": 10       },       {          "eid":1,          "name":"elaine",          "skillLevel": 2       },       {          "eid":2,          "name":"kieran",          "skillLevel": 11       },       {          "eid":3,          "name":"maeve",          "skillLevel": 10       },       {          "eid":4,          "name":"steve",          "skillLevel": 9       },       {          "eid":5,          "name":"steve",          "skillLevel": 9       },       {          "eid":6,          "name":"steve",          "skillLevel": 15       },       {          "eid":7,          "name":"amy",          "skillLevel": 11       }    ] } 

I should not be getting this error as I am doing an even simpler version of the cloud balancing application, if anyone can figure out where I am going wrong that would be a big help

All answers to this question, which has the identifier 61239158

The best answer:

Put a breakpoint in your RosterController, just before you call solverManager.solve(...). You'll see that your Roster instance has a shiftList field that is null.

The problem is in the json unmarshalling of your input data, because of naming mismatch in your input json. Note that by default Jackson in Spring Boot ignores properties that doesn't exist, instead of fail-fasting (a design decision I've never understood). There's a property to change that behavior IIRC.

Last questions

how do i remove the switch on my home screen?
how to edit the JS date and time to update atuomatically?
How to utilize data stored in a multidimensional array
Powermockito not mocking URL constructor in URI.toURL() method
Android Bluetooth LE Scanner only scans when phone's Location is turned on in some devices
docker wordpress container can't connect to mysql container
How can I declare a number in java that is more than 64-bits? [duplicate]
Optaplanner solutionClass entityCollectionProperty should never return null error when simple JSON object passed to controller
Anylogic, get the time a pedestrain is in a queue
How do I fix this syntax issue with my .flex file?
Optimizing query in PHP
How to find the highest number of a column and print two columns of that row in R?
Ideas on “Error: Type com.google.firebase.iid.zzav is referenced as an interface from com.google.firebase.messaging.zzd”?
JCIFS SmbFile.exists() and SmbFile.isDirectory() return false when it exists and I can listFiles()
PHP total order
Laravel booking system design
neural net - undefined column selected
How to indicate y axis does not start from 0 in ggplot?
Fragments in backStack
Spinner how to change the data