John Long
John Long

Reputation: 67

Illegal argument exception for Optaplanner project, solver config does not exist as a classpath resource in the class loader

I am getting an error when running my SpringBoot application in my solver class. I am trying to create the solver factory from an xml resource contained in the resources folder exactly like the employee rostering example for OptaPlanner but I am getting an Illegal argument exception that I can't figure out why. I tried moving the xml file into the same folder as the solver class but the same error appears. I don't know why the exception is being thrown when I am following the example code that OptaPlanner has for employee rostering ?

Below is my code for my solver class:

package com.schedule.demo.solver;

import com.schedule.demo.domain.Roster;
import com.schedule.demo.service.RosterService;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;

import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.impl.score.director.ScoreDirector;
import org.optaplanner.core.impl.score.director.ScoreDirectorFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.ApplicationScope;


@ApplicationScope
@Component
public class ScheduleSolverManager implements ApplicationRunner {

    public static final String SOLVER_CONFIG = "com/schedule/demo/service/solver/scheduleSolverConfig.xml";

    protected final transient Logger logger = LoggerFactory.getLogger(getClass());

    private SolverFactory<Roster> rosterSolverFactory;
    private ScoreDirectorFactory<Roster> rosterScoreDirectorFactory;

    private ThreadPoolTaskExecutor taskExecutor;

    private ConcurrentMap<Integer, Solver<Roster>> deptIdToSolverMap = new ConcurrentHashMap<>();

    private RosterService rosterService;

    public ScheduleSolverManager(ThreadPoolTaskExecutor taskExecutor, RosterService rosterService){
        this.taskExecutor = taskExecutor;
        this.rosterService = rosterService;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        setupRosterSolverFactory();
    }

    private void setupRosterSolverFactory() {
        rosterSolverFactory = SolverFactory.createFromXmlResource("com/schedule/demo/service/solver/scheduleSolverConfig.xml", ScheduleSolverManager.class.getClassLoader());
        rosterScoreDirectorFactory = rosterSolverFactory.buildSolver().getScoreDirectorFactory();
    }

    public CountDownLatch solve(Integer deptID){
        final CountDownLatch solvingEndedLatch = new CountDownLatch(1);
        try{
            taskExecutor.execute(()->{
                Solver<Roster> rosterSolver = rosterSolverFactory.buildSolver();
                deptIdToSolverMap.put(deptID, rosterSolver);
                rosterSolver.addEventListener(event -> {
                    if(event.isEveryProblemFactChangeProcessed()){
                        logger.info("New best solution found for deptId ({}).", deptID);
                        Roster newRoster = event.getNewBestSolution();
                        rosterService.updateShiftsOfRoster(newRoster);
                    }
                });
                Roster roster = rosterService.buildRoster(deptID);
                try {
                    rosterSolver.solve(roster);
                    solvingEndedLatch.countDown();
                }   finally {
                    deptIdToSolverMap.remove(deptID);
                }
            });
        } catch (Throwable e) {
            logger.error("Error solving for deptId (" + deptID + ").", e);
        }
        return solvingEndedLatch;
    }

    public Roster getRoster(final Integer deptId){
        Solver<Roster> rosterSolver = deptIdToSolverMap.get(deptId);
        return rosterSolver == null ? null : rosterSolver.getBestSolution();
    }

    public ScoreDirector<Roster> getScoreDirector() {
        return rosterScoreDirectorFactory.buildScoreDirector();
    }
}

The error when run is as follows:

Caused by: java.lang.IllegalArgumentException: The solverConfigResource (com/schedule/demo/service/solver/scheduleSolverConfig.xml) does not exist as a classpath resource in the classLoader (org.springframework.boot.devtools.restart.classloader.RestartClassLoader@2302b8ed).

Upvotes: 0

Views: 548

Answers (2)

yurloc
yurloc

Reputation: 2358

You have to place the solver config XML under src/main/resources in order to be able to load it as a classpath resource.

So, if you load it using the resource name com/schedule/demo/service/solver/scheduleSolverConfig.xml, then the file has to be located at

src/main/resources/com/schedule/demo/service/solver/scheduleSolverConfig.xml

Upvotes: 1

Geoffrey De Smet
Geoffrey De Smet

Reputation: 27312

Use the optaplanner-spring-boot-starter. It simplifies Spring integration greatly.

Guide: https://github.com/spring-guides/getting-started-guides/pull/126

Video: https://www.youtube.com/watch?v=U2N02ReT9CI

If you don't want to use the starter, this code might fix it: SolverFactory.createFromXmlResource("com/schedule/demo/service/solver/scheduleSolverConfig.xml", Roster.class.getClassLoader()); (uses the classloader of Roster).

Upvotes: 1

Related Questions