Tonny Tc
Tonny Tc

Reputation: 930

OptaPlanner got java reflection error in Spring boot

I just tried to run an OptaPlanner project in Spring Boot, but there's only very simple text in OptaPlanner User Guide for Spring.

Actually, I think it is very easy to copy all domain objects, configuration files and drools files from an OptaPlanner project to Spring Boot project without any changes, but the only question is how to call Solver's solve method.

I made it run after Spring Boot startup with a class (named CommandLineAppStartupRunner) which implements CommandLineRunner interface, and I called solve method in its run method. Finally, I got an exception like follows:

Caused by: java.lang.IllegalArgumentException: Can not set org.optaplanner.core.api.score.buildin.hardmediumsoft.HardMediumSoftScore field springbootcloudbalance.domain.CloudBalance.score to springbootcloudbalance.domain.CloudBalance
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)
at java.lang.reflect.Field.get(Field.java:393)
at org.optaplanner.core.impl.domain.common.accessor.ReflectionFieldMemberAccessor.executeGetter(ReflectionFieldMemberAccessor.java:54)
at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.getScore(SolutionDescriptor.java:1071)
at org.optaplanner.core.impl.score.director.AbstractScoreDirector.cloneSolution(AbstractScoreDirector.java:212)
at org.optaplanner.core.impl.solver.scope.DefaultSolverScope.setWorkingSolutionFromBestSolution(DefaultSolverScope.java:230)
at org.optaplanner.core.impl.solver.AbstractSolver.solvingStarted(AbstractSolver.java:75)
at org.optaplanner.core.impl.solver.DefaultSolver.solvingStarted(DefaultSolver.java:210)
at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:190)
at springbootcloudbalance.CommandLineAppStartupRunner.run(CommandLineAppStartupRunner.java:55)
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:818)
... 10 common frames omitted

I checked the code, and found the exception throws because the object from field.getDeclaringClass() is a different instance from the one from var1.getClass(). I'm afraid it due to the implementation of java reflection conflicts between OptaPlanner and Spring Boot.

The version I used is as follows:

Upvotes: 1

Views: 764

Answers (3)

Geoffrey De Smet
Geoffrey De Smet

Reputation: 27312

Musa provided this answer, but someone deleted it, despite that the JIRA link is extremely relevant, as it points out which version of OptaPlanner will deal better with this problem:

"An issue was submitted to OptaPlanner to provide better error messages for such cases: https://issues.jboss.org/browse/PLANNER-1586. Feel free to add any comments or suggestions."

Upvotes: 0

Geoffrey De Smet
Geoffrey De Smet

Reputation: 27312

Nick's answer is correct. This is just to figure out what's going on.

This line means that optaplanner is extracting CloudBalance.getScore():

at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.getScore(SolutionDescriptor.java:1071)

This line means that it's using a ReflectionFieldMemberAccessor for that, which is just way to read a field through reflection (including private fields):

at org.optaplanner.core.impl.domain.common.accessor.ReflectionFieldMemberAccessor.executeGetter(ReflectionFieldMemberAccessor.java:54)

Now the error message is where it gets interesting:

Can not set ...HardMediumSoftScore field ...CloudBalance.score to ...CloudBalance

It looks like basically doing:

CloudBalance cloudBalance2 = cloudBalance.getScore();

Huh?

Upvotes: 0

n1ck
n1ck

Reputation: 259

Removing the spring-boot-devtools dependency fixes this error. Another SO question similar to this one explains it has something to do with different classloaders: Optaplanner's Drools working memory is empty. The accepted answer also mentions a possible fix:

To fix it, configure spring dev tools to load Drools libraries in the RestartClassLoader, together with the project's classes: using-boot-devtools-customizing-classload

Upvotes: 3

Related Questions