4

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)
Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120
Gaël Oberson
  • 603
  • 1
  • 5
  • 14
  • That last exception is very weird. Put a breakpoint in `BeanPropertyMemberAccessor.executeGetter()` and print out the `propertyType` and the `bean.getClass()` here, I want to understand that misconfiguration better so optaplanner can throw a nicer error message. – Geoffrey De Smet Mar 02 '16 at 12:45
  • As for your original problem, put a debug breakpoint in DroolsScoreDirector#resetKieSession and follow the trail of getWorkingFacts (which is getProblemFacts() and the planning entities) – Geoffrey De Smet Mar 02 '16 at 12:46
  • Do you do any classloading magic, such as OSGi? – Geoffrey De Smet Mar 02 '16 at 12:48
  • Nope, no magic here. I'm using 6.3.0-final in a Spring boot tomcat container. And I got it to work before, so classloading is okay. – Gaël Oberson Mar 02 '16 at 15:23
  • [They most definitely are published in Maven Central.](http://central.maven.org/maven2/org/optaplanner/optaplanner-core/6.3.0.Final/) (for all versions, not just this one). – Geoffrey De Smet Mar 02 '16 at 15:49
  • executeGetter: propertyName is "lectures" wich is my Planning Entity List, propertyType is Class (java.util.List) wich is correct. And bean.getClass() returns ***.on.solver.TimeTable, wich is my Solution Class. It seems all to be correct! – Gaël Oberson Mar 02 '16 at 16:48
  • just checked resetKieSession. The getProblemFacts returns an object List containing all instances that should be in it. I mean, my 7 Facts and 4 planning entity instances. But Drools still gets nothing. It's wired! I see all 11 calls to kieSession.insert being done ! – Gaël Oberson Mar 02 '16 at 17:05
  • About executeGetter: does `bean.getClass().getName() == 's element string` ? Are the loaded by the same classloaders? – Geoffrey De Smet Mar 03 '16 at 08:03
  • 1
    Yes, `bean.getClass().getName() == TimeTable.class.getName()` returns `true`. But no, using `bean.getClass().getClassLoader() == TimeTable.class.getClassLoader()` I see two different Classloaders. Should I ensure they are the same one? – Gaël Oberson Mar 03 '16 at 08:58
  • In my app I have everywhere an `org.springframework.boot.devtools.restart.classloader.RestartClassLoader` but in executeGetter a `sun.misc.Launcher$AppClassLoader` is used. So Optaplanner does some magic and apparently uses it. – Gaël Oberson Mar 03 '16 at 09:27

3 Answers3

4

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".

Gaël Oberson
  • 603
  • 1
  • 5
  • 14
  • This is the correct solution. It's probably a good idea to do the classloader trick as described in my answer regardless, as that fixes another related issue with Spring Boot. – Geoffrey De Smet Mar 22 '19 at 13:19
1

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.

Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120
  • I updated to 6.4.0.Beta2, and used exactly this code snippet. But I still get the spring Classloader in the `BeanPropertyMemberAccessor`, and the `sun.misc.Launcher.AppClassLoader` for my TimeTable class. crazy. Anyway, I dont think this is the issue, because `resetKieSession` gets the right `workingFacts` Collection, with all facts and Planing Entities, and I see all needed calls to `kieSession.insert()` being done. My rule still gets nothing. – Gaël Oberson Mar 04 '16 at 07:47
  • Maybe it's the rule - simplify it so it's impossible not to match on a planning entity. If you still get it then, add it here to the question. – Geoffrey De Smet Mar 04 '16 at 07:52
  • But the classloading issue intrigues me more. Specifying `` and `` should always work. In fact, `` is seriously disrecommended in certain environments (think JDK 9 Jigsaw currently etc). So I 'd like to reproduce this issue - would it be possible to [submit a jira](https://issues.jboss.org/projects/PLANNER) with a reproducer for that? – Geoffrey De Smet Mar 04 '16 at 07:54
0

The problem should be resolved in Drools 7.23.0.Final. See https://issues.jboss.org/browse/DROOLS-1540.

yurloc
  • 2,268
  • 1
  • 15
  • 20