问题
I got optaplanner working correctly with drools rules. "Suddenly", after some change I did, Optaplanner does not put my facts in the drools kSession anymore.
I put some logging, and I see that optaplanner calls the getProblemFacts() method on my Solution, and this method returns a list with size > 0.
I wrote a DRL rule to simply count the facts and log these counts (This rule is unit tested, and works well when I put the objects in the ksession myself). I am also convinced that optaplanner does not put the facts in the working memory.
The ConstructionHeuristics phase terminates well (and does it's job, as my PlaningVariables are not null anymore after this phase). I got my issue only when LocalSearch begins.
Don't know how/where to search further to understand the issue. Any ideas?
I have an advice: I use <scanAnnotatedClasses/>
and have this problem.
If I put the two classes "manually" using <solutionClass/>
and <entityClass/>
then I get a reflection error:
Exception in thread "Solver" java.lang.IllegalArgumentException: object is not an instance of declaring class
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.optaplanner.core.impl.domain.common.accessor.BeanPropertyMemberAccessor.executeGetter(BeanPropertyMemberAccessor.java:67)
at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.extractEntityCollection(SolutionDescriptor.java:626)
at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.getEntityCount(SolutionDescriptor.java:489)
at org.optaplanner.core.impl.domain.solution.cloner.FieldAccessingSolutionCloner$FieldAccessingSolutionClonerRun.cloneSolution(FieldAccessingSolutionCloner.java:200)
at org.optaplanner.core.impl.domain.solution.cloner.FieldAccessingSolutionCloner.cloneSolution(FieldAccessingSolutionCloner.java:70)
at org.optaplanner.core.impl.score.director.AbstractScoreDirector.cloneSolution(AbstractScoreDirector.java:147)
at org.optaplanner.core.impl.solver.scope.DefaultSolverScope.setWorkingSolutionFromBestSolution(DefaultSolverScope.java:197)
at org.optaplanner.core.impl.solver.DefaultSolver.solvingStarted(DefaultSolver.java:195)
at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:175)
at ****.services.impl.SolverServiceImpl.lambda$0(SolverServiceImpl.java:169)
回答1:
I am using the spring dev tools to auto-reload my webapp uppon changes in the source files.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
This is the issue. To perform hot reloading, all resources and classes of the project are loaded and watched by spring's RestartClassLoader
but the librairies (dependencies, e.g. Drools & Optaplanner) are loaded by the Base ClassLoader (in fact AppClassLoader
). Therefore the problems.
To fix it, configure spring dev tools to load Drools librairies in the RestartClassLoader, together with the project's classes: using-boot-devtools-customizing-classload
So my question was not really good named. Drools working memory is not empty, but contains Objects that are no instanceof
my Classes, because not in the same ClassLoader.
To understand this I used the following Rule:
rule "countProblemFacts"
when
$nLectures : Long() from accumulate($lectures : Lecture(), count( $lectures ))
$nCourses : Long() from accumulate($courses : Course(), count( $courses ))
$nRooms : Long() from accumulate($rooms : Room(), count( $rooms ))
$nPeriods : Long() from accumulate($periods : Period(), count( $periods ))
$nObjects : Long() from accumulate($objects : Object(), count( $objects ))
then
DroolsUtil.log(drools, "Drools working memory");
DroolsUtil.log("Lectures:", $nLectures);
DroolsUtil.log("Courses:", $nCourses);
DroolsUtil.log("Rooms:", $nRooms);
DroolsUtil.log("Periods:", $nPeriods);
DroolsUtil.log("Objects:", $nObjects);
DroolsUtil.log(drools, "Total", ($nLectures + $nCourses + $nRooms + $nPeriods), "objects");
end
$nObjects counts to 12, all others count to 0, as the Classes are not "the same".
回答2:
In my app I have everywhere an
org.springframework.boot.devtools.restart.classloader.RestartClassLoader
That's not the default classloader, so there is classloading magic going on. According to your comment, it's not the same classloader as used to load the optaplanner classes. So you'll need to provide your classloader:
Classloader classloader = TimeTable.class.getClassLoader();
... = SolverFactory.createFromXmlResource(".../solverConfig.xml", classloader);
It might be needed to upgrade to 6.4.0.Beta2
, I 've fixed a number of advanced classloading issues last month.
回答3:
The problem should be resolved in Drools 7.23.0.Final. See https://issues.jboss.org/browse/DROOLS-1540.
来源:https://stackoverflow.com/questions/35746370/optaplanners-drools-rules-dont-fire-with-spring-boots-devtools-on-the-classpa