I’m migrating Optaplanner from v7.x to v8.14 on a project that uses spring-boot (as a web api).
I’ve added the optaplanner-spring-boot-starter dependency to the project, however when starting the application, I got the following error :
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'solverManager' defined in class path resource [org/optaplanner/spring/boot/autoconfigure/OptaPlannerAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.optaplanner.core.api.solver.SolverManager]: Factory method 'solverManager' threw exception; nested exception is java.lang.IllegalStateException: The solutionClass (class be.my.domain.MySolution) has been specified as a solution in the configuration, but does not have a @PlanningSolution annotation. at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:627) at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:607) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1321) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1160) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142) at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) at be.my.WebgardesApplication.main(WebgardesApplication.java:33) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:564) at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.optaplanner.core.api.solver.SolverManager]: Factory method 'solverManager' threw exception; nested exception is java.lang.IllegalStateException: The solutionClass (class be.my.domain.MySolution) has been specified as a solution in the configuration, but does not have a @PlanningSolution annotation. at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622) ... 24 common frames omitted Caused by: java.lang.IllegalStateException: The solutionClass (class be.my.domain.MySolution) has been specified as a solution in the configuration, but does not have a @PlanningSolution annotation. at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.processSolutionAnnotations(SolutionDescriptor.java:269) at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.processAnnotations(SolutionDescriptor.java:212) at org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor.buildSolutionDescriptor(SolutionDescriptor.java:120) at org.optaplanner.core.impl.solver.DefaultSolverFactory.buildSolutionDescriptor(DefaultSolverFactory.java:160) at org.optaplanner.core.impl.solver.DefaultSolverFactory.buildScoreDirectorFactory(DefaultSolverFactory.java:133) at org.optaplanner.core.impl.solver.DefaultSolverFactory.buildSolver(DefaultSolverFactory.java:87) at org.optaplanner.core.impl.solver.DefaultSolverManager.validateSolverFactory(DefaultSolverManager.java:69) at org.optaplanner.core.impl.solver.DefaultSolverManager.<init>(DefaultSolverManager.java:58) at org.optaplanner.core.api.solver.SolverManager.create(SolverManager.java:111) at org.optaplanner.spring.boot.autoconfigure.OptaPlannerAutoConfiguration.solverManager(OptaPlannerAutoConfiguration.java:98) at org.optaplanner.spring.boot.autoconfigure.OptaPlannerAutoConfiguration$$EnhancerBySpringCGLIB$$8803e22a.CGLIB$solverManager$2(<generated>) at org.optaplanner.spring.boot.autoconfigure.OptaPlannerAutoConfiguration$$EnhancerBySpringCGLIB$$8803e22a$$FastClassBySpringCGLIB$$40c364cf.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) at org.optaplanner.spring.boot.autoconfigure.OptaPlannerAutoConfiguration$$EnhancerBySpringCGLIB$$8803e22a.solverManager(<generated>) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:564) at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ... 25 common frames omitted Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.optaplanner.core.api.solver.SolverManager]: Factory method 'solverManager' threw exception; nested exception is java.lang.IllegalStateException: The solutionClass (class be.my.domain.MySolution) has been specified as a solution in the configuration, but does not have a @PlanningSolution annotation. Caused by: java.lang.IllegalStateException: The solutionClass (class be.my.domain.MySolution) has been specified as a solution in the configuration, but does not have a @PlanningSolution annotation.
The project is in two separated modules, one with everything related to Optaplanner (the config.xml, the .drl, the Solution, etc), the other is the spring-boot web api.
Here are the dependencies (.gradle) for both :
module “my-roster”
dependencies { implementation 'org.springframework:spring-web:5.1.6.RELEASE' implementation 'org.springframework:spring-context:5.0.10.RELEASE' implementation 'org.springframework.boot:spring-boot:2.1.4.RELEASE' // This dependency is used internally, and not exposed to consumers on their own compile classpath. implementation 'com.google.guava:guava:27.0.1-jre' implementation "org.mapstruct:mapstruct:1.3.0.Final" annotationProcessor "org.mapstruct:mapstruct-processor:1.3.0.Final" implementation "org.optaplanner:optaplanner-core:${optaplannerVersion}" implementation "org.optaplanner:optaplanner-persistence-xstream:${optaplannerVersion}" implementation "org.optaplanner:optaplanner-spring-boot-starter:${optaplannerVersion}" implementation 'com.fasterxml.jackson.module:jackson-module-afterburner:2.11.0' implementation "org.zalando:problem-spring-web:0.24.0-RC.0" implementation "net.logstash.logback:logstash-logback-encoder:5.2" runtimeOnly 'org.springframework.boot:spring-boot-devtools:2.1.4.RELEASE' // Use JUnit test framework testImplementation 'junit:junit:4.12' testAnnotationProcessor "org.mapstruct:mapstruct-processor:1.3.0.Final" compile 'org.apache.httpcomponents:httpcore:4.4.1' compile 'org.apache.httpcomponents:httpclient:4.5.12' implementation 'org.json:org.json:chargebee-1.0' implementation 'org.apache.commons:commons-lang3:3.0' }
module “web-app”
dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-aop' implementation 'org.springframework.boot:spring-boot-starter-data-rest' annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' annotationProcessor "org.mapstruct:mapstruct-processor:1.3.0.Final" implementation 'com.fasterxml.jackson.module:jackson-module-afterburner' implementation "org.zalando:problem-spring-web:0.24.0-RC.0" implementation "net.logstash.logback:logstash-logback-encoder:5.2" implementation "org.optaplanner:optaplanner-core:${optaplannerVersion}" implementation "org.optaplanner:optaplanner-persistence-xstream:${optaplannerVersion}" implementation "org.optaplanner:optaplanner-spring-boot-starter:${optaplannerVersion}" implementation project(':my-roster') runtimeOnly 'org.springframework.boot:spring-boot-devtools' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.7' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.9.7' compile 'org.apache.httpcomponents:httpcore:4.4.1' compile 'org.apache.httpcomponents:httpclient:4.5.12' implementation 'org.json:org.json:chargebee-1.0' }
the SolverConfig file :
<solver xmlns="https://www.optaplanner.org/xsd/solver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://www.optaplanner.org/xsd/solver https://www.optaplanner.org/xsd/solver/solver.xsd"> <solutionClass>be.my.domain.MySolution</solutionClass> <entityClass>be.my.domain.DutyAssignment</entityClass> ... </solver>
And the MySolution file :
package be.my.domain; // Imports ... @PlanningSolution @XStreamAlias("MySolution") public class MySolution extends AbstractPersistable { private static final long serialVersionUID = 1L; private String code; //All the @ProblemFactProperty / CollectionProperty and the ProblemEntityCollectionProperty @PlanningScore private HardMediumSoftScore score; public MySolution(){} //A lot of getters }
EDIT : The @PlanningSolution class and the configuration files are in one module, the solverConfig, solverFactory, scoreManager, etc. are all built in the other package, could this be an issue in v8+ ?
Advertisement
Answer
This is a bug in Optaplanner, the current workaround is to move the needed code from the different modules into one. Not ideal, but it works. To follow the issue : https://issues.redhat.com/browse/PLANNER-2600 Once resolved, this bug should not happen anymore.