I am working on creating a REST API using Timefold for Quarkus and running it as a native build on Windows-based GraalVM.
I have implemented a POST method named /evaluate, which calculates scores instead of executing jobs. When running it with the mvn quarkus:dev command on the JVM, it works correctly. However, when executing the native build, an error occurs when the POST method is called.
@Operation(summary = "Calculate and return a score for the allocation schedule.")
@APIResponses(value = {
@APIResponse(
responseCode = "200",
description = "Schedule scores.",
content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = StaffScheduleEvaluationResponse.class))
),
@APIResponse(responseCode = "404", description = "Score not found.",
content = @Content(mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(implementation = ErrorInfo.class))),
@APIResponse(responseCode = "500", description = "A problem occurred while processing the score.",
content = @Content(mediaType = MediaType.APPLICATION_JSON,
schema = @Schema(implementation = ErrorInfo.class)))
})
@POST
@Path("/evaluate")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public StaffScheduleEvaluationResponse getEvaluate(StaffSchedule problem) {
// Score Calculation
SolverFactory<StaffSchedule> actualSolverFactory = SolverFactory.createFromXmlResource("org/acme/staffscheduling/solverConfig.xml");
SolutionManager<StaffSchedule, HardMediumSoftScore> solutionManager = SolutionManager.create(actualSolverFactory);
ScoreAnalysis<HardMediumSoftScore> scoreAnalysis = solutionManager.analyze(problem);
ScoreAnalysisResult scoreAnalysisResult = generateScoreAnalysis(scoreAnalysis, problem);
return new StaffScheduleEvaluationResponse(
scoreAnalysis.score(),
scoreAnalysisResult
);
}
The hierarchy under the resource directory is as follows:
resources
├ org/acme/staffscheduling
│ └ solverConfig.xml
├ META-INF/native-image
│ └ reflect-config.json
└ application.properties
solverConfig.xml:
<?xml version="1.0" encoding="UTF-8"?>
<solver xmlns="https://timefold.ai/xsd/solver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://timefold.ai/xsd/solver https://timefold.ai/xsd/solver/solver.xsd">
<!-- Domain model configuration -->
<solutionClass>org.acme.staffscheduling.domain.StaffSchedule</solutionClass>
<entityClass>org.acme.staffscheduling.domain.StaffAssignment</entityClass>
<!-- Score configuration -->
<scoreDirectorFactory>
<constraintProviderClass>org.acme.staffscheduling.solver.StaffScheduleConstraintProvider</constraintProviderClass>
</scoreDirectorFactory>
<!-- Optimization algorithms configuration -->
<termination>
<terminationCompositionStyle>OR</terminationCompositionStyle>
</termination>
<constructionHeuristic>
<constructionHeuristicType>ALLOCATE_ENTITY_FROM_QUEUE</constructionHeuristicType>
<entitySorterManner>DECREASING_DIFFICULTY_IF_AVAILABLE</entitySorterManner>
<valueSorterManner>DECREASING_STRENGTH_IF_AVAILABLE</valueSorterManner>
</constructionHeuristic>
<localSearch>
<localSearchType>TABU_SEARCH</localSearchType>
<unionMoveSelector>
<changeMoveSelector>
<fixedProbabilityWeight>1.0</fixedProbabilityWeight>
</changeMoveSelector>
<swapMoveSelector>
<fixedProbabilityWeight>1.0</fixedProbabilityWeight>
</swapMoveSelector>
<cartesianProductMoveSelector>
<fixedProbabilityWeight>2.0</fixedProbabilityWeight>
<changeMoveSelector/>
<changeMoveSelector/>
<swapMoveSelector/>
</cartesianProductMoveSelector>
<pillarChangeMoveSelector>
<fixedProbabilityWeight>1.0</fixedProbabilityWeight>
<subPillarType>ALL</subPillarType>
<pillarSelector>
<minimumSubPillarSize>2</minimumSubPillarSize>
<maximumSubPillarSize>4</maximumSubPillarSize>
</pillarSelector>
</pillarChangeMoveSelector>
<pillarSwapMoveSelector>
<fixedProbabilityWeight>1.0</fixedProbabilityWeight>
<subPillarType>ALL</subPillarType>
<pillarSelector>
<minimumSubPillarSize>2</minimumSubPillarSize>
<maximumSubPillarSize>4</maximumSubPillarSize>
</pillarSelector>
</pillarSwapMoveSelector>
</unionMoveSelector>
</localSearch>
</solver>
reflect-config.json:
[
{
"name": "ai.timefold.solver.core.api.solver.SolverFactory",
"allDeclaredMethods": true,
"allDeclaredFields": true,
"allDeclaredConstructors": true
},
{
"name": "ai.timefold.solver.core.config.solver.SolverConfig",
"allDeclaredMethods": true,
"allDeclaredFields": true,
"allDeclaredConstructors": true
}
]
application.properties:
########################
# Timefold Solver properties
########################
quarkus.timefold.solver.termination.spent-limit=300s
quarkus.arc.context-propagation.enabled=true
quarkus.log.category."ai.timefold.solver".min-level=TRACE
%dev.quarkus.log.category."ai.timefold.solver".level=DEBUG
%prod.quarkus.log.category."ai.timefold.solver".level=INFO
quarkus.log.category."ai.timefold.solver".level=TRACE
# Domain model configuration
# Solution class for the optimization problem
quarkus.timefold.solver.solution-class=org.acme.staffscheduling.domain.StaffSchedule
# Entity class to represent planning variables
quarkus.timefold.solver.entity-classes=org.acme.staffscheduling.domain.StaffAssignment
# Score Director configuration
# Constraint Provider class to define constraints
quarkus.timefold.solver.score-director-factory.constraint-provider-class=org.acme.staffscheduling.solver.StaffScheduleConstraintProvider
quarkus.timefold.solver-config-xml=org/acme/staffscheduling/solverConfig.xml
# Termination conditions
# Multiple termination conditions are combined with OR logic
quarkus.timefold.solver.termination.composition-style=OR
# Maximum time without improvement to terminate solving
quarkus.timefold.solver.termination.unimproved-spent-limit=60s
########################
# Native build properties
########################
# Enable Swagger UI also in the native mode
quarkus.swagger-ui.always-include=true
quarkus.native.resources.includes=solver.xsd,org/acme/staffscheduling/solverConfig.xml
########################
# CORS Configuration
########################
# Enable CORS
quarkus.http.cors=true
# Allow all origins (modify as needed for security)
quarkus.http.cors.origins=*
# Allow specific HTTP methods
quarkus.http.cors.methods=GET,POST,PUT,DELETE,OPTIONS
# Allow specific headers in requests
quarkus.http.cors.headers=Authorization,Content-Type
# Expose specific headers to clients
quarkus.http.cors.exposed-headers=Authorization
## Cache preflight requests for 24 hours
#quarkus.http.cors.access-control-max-age=24H
########################
# logging file
########################
quarkus.log.file.enable=true
quarkus.log.file.path=logs/application.log
quarkus.log.file.format=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{2.}] (%t) %s%e%n
quarkus.log.file.level=TRACE
quarkus.log.file.rotation.max-file-size=10M
quarkus.log.file.rotation.max-backup-index=5
error log:
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
・・・
2024-12-27 17:23:52,271 INFO [ai.tim.sol.qua.bea.TimefoldSolverBannerBean] (main) Using Timefold Solver Community Edition v1.16.0.
2024-12-27 17:23:52,273 DEBUG [io.net.buf.PooledByteBufAllocator] (vert.x-eventloop-thread-0) -Dio.netty.allocator.numHeapArenas: 16
2024-12-27 17:23:52,273 DEBUG [io.net.buf.PooledByteBufAllocator] (vert.x-eventloop-thread-0) -Dio.netty.allocator.numDirectArenas: 16
2024-12-27 17:23:52,273 DEBUG [io.net.buf.PooledByteBufAllocator] (vert.x-eventloop-thread-0) -Dio.netty.allocator.pageSize: 8192
2024-12-27 17:23:52,273 DEBUG [io.net.buf.PooledByteBufAllocator] (vert.x-eventloop-thread-0) -Dio.netty.allocator.maxOrder: 3
2024-12-27 17:23:52,273 DEBUG [io.net.buf.PooledByteBufAllocator] (vert.x-eventloop-thread-0) -Dio.netty.allocator.chunkSize: 65536
2024-12-27 17:23:52,274 DEBUG [io.net.buf.PooledByteBufAllocator] (vert.x-eventloop-thread-0) -Dio.netty.allocator.smallCacheSize: 256
2024-12-27 17:23:52,274 DEBUG [io.net.buf.PooledByteBufAllocator] (vert.x-eventloop-thread-0) -Dio.netty.allocator.normalCacheSize: 64
2024-12-27 17:23:52,274 DEBUG [io.net.buf.PooledByteBufAllocator] (vert.x-eventloop-thread-0) -Dio.netty.allocator.maxCachedBufferCapacity: 32768
2024-12-27 17:23:52,274 DEBUG [io.net.buf.PooledByteBufAllocator] (vert.x-eventloop-thread-0) -Dio.netty.allocator.cacheTrimInterval: 8192
2024-12-27 17:23:52,274 DEBUG [io.net.buf.PooledByteBufAllocator] (vert.x-eventloop-thread-0) -Dio.netty.allocator.cacheTrimIntervalMillis: 0
2024-12-27 17:23:52,274 DEBUG [io.net.buf.PooledByteBufAllocator] (vert.x-eventloop-thread-0) -Dio.netty.allocator.useCacheForAllThreads: false
2024-12-27 17:23:52,275 DEBUG [io.net.buf.PooledByteBufAllocator] (vert.x-eventloop-thread-0) -Dio.netty.allocator.maxCachedByteBuffersPerChunk: 1023
2024-12-27 17:23:52,275 DEBUG [io.net.buf.ByteBufUtil] (vert.x-eventloop-thread-0) -Dio.netty.allocator.type: pooled
2024-12-27 17:23:52,275 DEBUG [io.net.buf.ByteBufUtil] (vert.x-eventloop-thread-0) -Dio.netty.threadLocalDirectBufferSize: 0
2024-12-27 17:23:52,275 DEBUG [io.net.buf.ByteBufUtil] (vert.x-eventloop-thread-0) -Dio.netty.maxThreadLocalCharBufferSize: 16384
2024-12-27 17:23:52,275 DEBUG [io.net.boo.ChannelInitializerExtensions] (vert.x-eventloop-thread-0) -Dio.netty.bootstrap.extensions: null
2024-12-27 17:23:52,276 INFO [io.quarkus] (main) staff-scheduling 1.0.0-SNAPSHOT native (powered by Quarkus 3.16.3) started in 0.159s. Listening on: http://0.0.0.0:8080
2024-12-27 17:23:52,277 INFO [io.quarkus] (main) Profile prod activated.
2024-12-27 17:23:52,277 INFO [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, hibernate-orm-panache, jdbc-postgresql, narayana-jta, resteasy, resteasy-jackson, smallrye-context-propagation, smallrye-openapi, swagger-ui, timefold-solver, timefold-solver-jackson, vertx]
2024-12-27 17:24:21,337 DEBUG [org.jbo.res.res.i18n] (executor-thread-1) RESTEASY002315: PathInfo: /schedules/evaluate
2024-12-27 17:24:21,340 DEBUG [org.jbo.res.res.i18n] (executor-thread-1) Interceptor Context: org.jboss.resteasy.core.interception.jaxrs.ServerReaderInterceptorContext, Method : proceed
2024-12-27 17:24:21,340 DEBUG [org.jbo.res.res.i18n] (executor-thread-1) MessageBodyReader: org.jboss.resteasy.core.providerfactory.SortedKey
2024-12-27 17:24:21,341 DEBUG [org.jbo.res.res.i18n] (executor-thread-1) MessageBodyReader: org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider
2024-12-27 17:24:21,341 DEBUG [org.jbo.res.res.i18n] (executor-thread-1) Provider : org.jboss.resteasy.plugins.providers.jackson.ResteasyJackson2Provider, Method : readFrom
2024-12-27 17:24:21,404 FINE [jak.xml.bind] (executor-thread-1) Checking system property jakarta.xml.bind.JAXBContextFactory
2024-12-27 17:24:21,404 FINE [jak.xml.bind] (executor-thread-1) not found
2024-12-27 17:24:21,405 FINE [jak.xml.bind] (executor-thread-1) Trying to create the platform default provider
2024-12-27 17:24:21,405 FINE [jak.xml.bind] (executor-thread-1) Unable to find from OSGi: [jakarta.xml.bind.JAXBContextFactory]: java.lang.ClassNotFoundException: org.glassfish.hk2.osgiresourcelocator.ServiceLoader. This exception was synthesized during native image building from a call to java.lang.Class.forName(String) with constant arguments.
at jakarta.xml.bind.ServiceLoaderUtil.lookupUsingOSGiServiceLoader(ServiceLoaderUtil.java:57)
at jakarta.xml.bind.ContextFinder.find(ContextFinder.java:378)
at jakarta.xml.bind.JAXBContext.newInstance(JAXBContext.java:605)
at jakarta.xml.bind.JAXBContext.newInstance(JAXBContext.java:546)
at ai.timefold.solver.core.impl.io.jaxb.GenericJaxbIO.<init>(GenericJaxbIO.java:76)
at ai.timefold.solver.core.impl.io.jaxb.GenericJaxbIO.<init>(GenericJaxbIO.java:68)
at ai.timefold.solver.core.impl.io.jaxb.SolverConfigIO.<init>(SolverConfigIO.java:12)
at ai.timefold.solver.core.config.solver.SolverConfig.createFromXmlReader(SolverConfig.java:194)
at ai.timefold.solver.core.config.solver.SolverConfig.createFromXmlInputStream(SolverConfig.java:171)
at ai.timefold.solver.core.config.solver.SolverConfig.createFromXmlResource(SolverConfig.java:117)
at ai.timefold.solver.core.config.solver.SolverConfig.createFromXmlResource(SolverConfig.java:92)
at ai.timefold.solver.core.api.solver.SolverFactory.createFromXmlResource(SolverFactory.java:42)
at org.acme.staffscheduling.rest.StaffScheduleResource.getEvaluate(StaffScheduleResource.java:647)
at java.base@21.0.5/java.lang.reflect.Method.invoke(Method.java:580)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:154)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:118)
at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:560)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:452)
at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:413)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:321)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:415)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:378)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:356)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:70)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:429)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invokePropagateNotFound$6(SynchronousDispatcher.java:275)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:154)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:321)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:157)
at org.jboss.resteasy.core.SynchronousDispatcher.invokePropagateNotFound(SynchronousDispatcher.java:260)
at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:86)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:97)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:627)
at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2675)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2654)
at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1627)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1594)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base@21.0.5/java.lang.Thread.runWith(Thread.java:1596)
at java.base@21.0.5/java.lang.Thread.run(Thread.java:1583)
at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:896)
at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:872)
2024-12-27 17:24:21,412 FINE [jak.xml.bind] (executor-thread-1) Trying to create the platform default provider
2024-12-27 17:24:21,412 FINE [jak.xml.bind] (executor-thread-1) loaded org.glassfish.jaxb.runtime.v2.ContextFactory from null
2024-12-27 17:24:21,412 FINE [jak.xml.bind] (executor-thread-1) Using jakarta.xml.bind-api on the class path.
2024-12-27 17:24:21,413 FINE [org.gla.jax.run.v2.ContextFactory] (executor-thread-1) Property org.glassfish.jaxb.XmlAccessorFactoryis not active. Using JAXB's implementation
2024-12-27 17:24:21,431 DEBUG [io.ver.ext.web.RoutingContext] (executor-thread-1) RoutingContext failure (500): org.jboss.resteasy.spi.UnhandledException: java.lang.IllegalArgumentException: The scoreDirectorFactory lacks configuration for either constraintProviderClass, easyScoreCalculatorClass or incrementalScoreCalculatorClass.
at org.jboss.resteasy.core.ExceptionHandler.handleApplicationException(ExceptionHandler.java:107)
at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:344)
at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:205)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:452)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invokePropagateNotFound$6(SynchronousDispatcher.java:275)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:154)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:321)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:157)
at org.jboss.resteasy.core.SynchronousDispatcher.invokePropagateNotFound(SynchronousDispatcher.java:260)
at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:86)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:97)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:627)
at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2675)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2654)
at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1627)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1594)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base@21.0.5/java.lang.Thread.runWith(Thread.java:1596)
at java.base@21.0.5/java.lang.Thread.run(Thread.java:1583)
at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:896)
at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:872)
Caused by: java.lang.IllegalArgumentException: The scoreDirectorFactory lacks configuration for either constraintProviderClass, easyScoreCalculatorClass or incrementalScoreCalculatorClass.
at ai.timefold.solver.core.impl.score.director.ScoreDirectorFactoryFactory.decideMultipleScoreDirectorFactories(ScoreDirectorFactoryFactory.java:78)
at ai.timefold.solver.core.impl.score.director.ScoreDirectorFactoryFactory.buildScoreDirectorFactory(ScoreDirectorFactoryFactory.java:26)
at ai.timefold.solver.core.impl.solver.DefaultSolverFactory.buildScoreDirectorFactory(DefaultSolverFactory.java:191)
at ai.timefold.solver.core.impl.solver.DefaultSolverFactory.<init>(DefaultSolverFactory.java:70)
at ai.timefold.solver.core.api.solver.SolverFactory.createFromXmlResource(SolverFactory.java:43)
at org.acme.staffscheduling.rest.StaffScheduleResource.getEvaluate(StaffScheduleResource.java:647)
at java.base@21.0.5/java.lang.reflect.Method.invoke(Method.java:580)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:154)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:118)
at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:560)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:452)
at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:413)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:321)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:415)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:378)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:356)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:70)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:429)
... 20 more
2024-12-27 17:24:21,437 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /schedules/evaluate failed, error id: a5a547d3-9aa1-4236-aa88-eeb66ef227f1-1: org.jboss.resteasy.spi.UnhandledException: java.lang.IllegalArgumentException: The scoreDirectorFactory lacks configuration for either constraintProviderClass, easyScoreCalculatorClass or incrementalScoreCalculatorClass.
at org.jboss.resteasy.core.ExceptionHandler.handleApplicationException(ExceptionHandler.java:107)
at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:344)
at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:205)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:452)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invokePropagateNotFound$6(SynchronousDispatcher.java:275)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:154)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:321)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:157)
at org.jboss.resteasy.core.SynchronousDispatcher.invokePropagateNotFound(SynchronousDispatcher.java:260)
at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:86)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151)
at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:97)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:627)
at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2675)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2654)
at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1627)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1594)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base@21.0.5/java.lang.Thread.runWith(Thread.java:1596)
at java.base@21.0.5/java.lang.Thread.run(Thread.java:1583)
at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:896)
at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:872)
Caused by: java.lang.IllegalArgumentException: The scoreDirectorFactory lacks configuration for either constraintProviderClass, easyScoreCalculatorClass or incrementalScoreCalculatorClass.
at ai.timefold.solver.core.impl.score.director.ScoreDirectorFactoryFactory.decideMultipleScoreDirectorFactories(ScoreDirectorFactoryFactory.java:78)
at ai.timefold.solver.core.impl.score.director.ScoreDirectorFactoryFactory.buildScoreDirectorFactory(ScoreDirectorFactoryFactory.java:26)
at ai.timefold.solver.core.impl.solver.DefaultSolverFactory.buildScoreDirectorFactory(DefaultSolverFactory.java:191)
at ai.timefold.solver.core.impl.solver.DefaultSolverFactory.<init>(DefaultSolverFactory.java:70)
at ai.timefold.solver.core.api.solver.SolverFactory.createFromXmlResource(SolverFactory.java:43)
at org.acme.staffscheduling.rest.StaffScheduleResource.getEvaluate(StaffScheduleResource.java:647)
at java.base@21.0.5/java.lang.reflect.Method.invoke(Method.java:580)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:154)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:118)
at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:560)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:452)
at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:413)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:321)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:415)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:378)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:356)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:70)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:429)
... 20 more
If anyone could help me resolve this issue, I would greatly appreciate it.
After the revision:
By following the advice you kindly provided, I was able to resolve the issue with the following adjustments.
quarkus.timefold.solver.score-director-factory.constraint-provider-class (in application.properties)
<scoreDirectorFactory>, <solutionClass> and <entityClass> (in solverConfig.xml)
SolverManager<StaffSchedule, String> solverManager;
SolutionManager<StaffSchedule, HardMediumSoftScore> solutionManager;
SolverFactory<StaffSchedule> solverFactory;
@Inject
public StaffScheduleResource(SolverManager<StaffSchedule, String> solverManager, SolutionManager<StaffSchedule, HardMediumSoftScore> solutionManager, SolverFactory<StaffSchedule> solverFactory) {
this.solverManager = solverManager;
this.solutionManager = solutionManager;
this.solverFactory = solverFactory;
}
/** Attempt 1: To use the CDI proxy of solverFactory, the following line was removed: */
// SolverFactory<StaffSchedule> actualSolverFactory = SolverFactory.createFromXmlResource("org/acme/staffscheduling/solverConfig.xml");
// SolutionManager<StaffSchedule, HardMediumSoftScore> solutionManager = SolutionManager.create(actualSolverFactory);
/** Attempt 2: A ClassCastException occurred when attempting to use the CDI proxy of solverFactory with SolutionManager.create. This line turned out to be unnecessary.*/
// SolutionManager<StaffSchedule, HardMediumSoftScore> solutionManager = SolutionManager.create(solverFactory);
ScoreAnalysis<HardMediumSoftScore> scoreAnalysis = solutionManager.analyze(problem);
at ai.timefold.solver.core.impl.solver.DefaultSolverFactory.(DefaultSolverFactory.java:70) at ai.timefold.solver.core.api.solver.SolverFactory.createFromXmlResource(SolverFactory.java:43) at org.acme.staffscheduling.rest.StaffScheduleResource.getEvaluate(StaffScheduleResource.java:647)
That SolverFactory.createX call is weird. In quarkus, you should just @Inject SolverManager, SolutionManager or SolverFactory
, if the timefold-solver-quarkus
dependency is in your pom.xml
. Let the timefold quarkus extension figure out how to create those.
I suspect that StaffScheduleResource.java line 647
isn't even using org/acme/staffscheduling/solverConfig.xml
! Otherwise the error message makes no sense at all.