Created
November 1, 2019 09:47
-
-
Save ge0ffrey/2275ef5670e1245750e3be3fc23fbc34 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2019 Red Hat, Inc. and/or its affiliates. | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package org.optaplanner.core.api.solver.manager; | |
import java.util.Map; | |
import java.util.UUID; | |
import java.util.concurrent.ThreadFactory; | |
import java.util.function.Consumer; | |
import org.optaplanner.core.api.domain.entity.PlanningEntity; | |
import org.optaplanner.core.api.domain.solution.PlanningSolution; | |
import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty; | |
import org.optaplanner.core.api.score.Score; | |
import org.optaplanner.core.api.score.constraint.Indictment; | |
import org.optaplanner.core.api.solver.Solver; | |
import org.optaplanner.core.api.solver.SolverFactory; | |
import org.optaplanner.core.api.solver.event.BestSolutionChangedEvent; | |
import org.optaplanner.core.config.solver.SolverConfig; | |
import org.optaplanner.core.impl.score.director.ScoreDirector; | |
import org.optaplanner.core.impl.solver.DefaultSolverFactory; | |
import org.optaplanner.core.impl.solver.ProblemFactChange; | |
import org.optaplanner.core.impl.solver.manager.DefaultSolverManager; | |
import org.optaplanner.core.impl.solver.termination.Termination; | |
/** | |
* A SolverManager solves multiple planning problems of the same domain, | |
* asynchronously without blocking the calling thread. | |
* <p> | |
* To create a SolverManager, use {@link #createFromXmlResource(String)}. | |
* To solve a planning problem, call {@link #solve(Object, Consumer, Consumer)} | |
* or {@link #solveAndListenToBestSolutions(Object, Consumer, Consumer)}. | |
* <p> | |
* These methods are thread-safe unless explicitly stated otherwise. | |
* <p> | |
* Internally a SolverManager manages a thread pool of solver threads (which call {@link Solver#solve(Object)}) | |
* and consumer threads (to handle the {@link BestSolutionChangedEvent}s). | |
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation | |
*/ | |
public interface SolverManager<Solution_> extends AutoCloseable { | |
// ************************************************************************ | |
// Static creation methods: XML | |
// ************************************************************************ | |
/** | |
* Reads an XML solver configuration from the classpath | |
* and uses that {@link SolverConfig} to build a {@link SolverFactory} and then a {@link SolverManager}. | |
* The XML root element must be {@code <solver>}. | |
* @param solverConfigResource never null, a classpath resource | |
* as defined by {@link ClassLoader#getResource(String)} | |
* @return never null | |
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation | |
*/ | |
static <Solution_> SolverManager<Solution_> createFromXmlResource(String solverConfigResource) { | |
SolverFactory<Solution_> solverFactory = SolverFactory.createFromXmlResource(solverConfigResource); | |
return new DefaultSolverManager<>(solverFactory); | |
} | |
/** | |
* As defined by {@link #createFromXmlResource(String)}. | |
* @param solverConfigResource never null, a classpath resource | |
* as defined by {@link ClassLoader#getResource(String)} | |
* @param classLoader sometimes null, the {@link ClassLoader} to use for loading all resources and {@link Class}es, | |
* null to use the default {@link ClassLoader} | |
* @return never null | |
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation | |
*/ | |
static <Solution_> SolverManager<Solution_> createFromXmlResource(String solverConfigResource, ClassLoader classLoader) { | |
SolverFactory<Solution_> solverFactory = SolverFactory.createFromXmlResource(solverConfigResource, classLoader); | |
return new DefaultSolverManager<>(solverFactory); | |
} | |
// TODO Do we need ThreadFactory threadFactory methods? If yes, we need SolverConfig.create*(..., threadFactory) first. | |
// ************************************************************************ | |
// Static creation methods: SolverFactory | |
// ************************************************************************ | |
// TODO add SolverManagerConfig to capture solverThreadCount, consumerThreadCount, throttlingDelay, threadFactory, congestionStrategy, etc properties | |
/** | |
* Uses a {@link SolverConfig} to build a {@link SolverManager}. | |
* If you don't need to manipulate the {@link SolverConfig} programmatically, | |
* use {@link #createFromXmlResource(String)} instead. | |
* @param solverConfig never null | |
* @return never null | |
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation | |
*/ | |
static <Solution_> SolverManager<Solution_> create(SolverConfig solverConfig) { | |
SolverFactory<Solution_> solverFactory = SolverFactory.create(solverConfig); | |
return new DefaultSolverManager<>(solverFactory); | |
} | |
// ************************************************************************ | |
// Interface methods | |
// ************************************************************************ | |
/** | |
* Submits a planning problem to solve and returns immediately. | |
* The planning problem is solved on a solver {@link Thread}, as soon as one is available. | |
* <p> | |
* When the solver terminates, the {@code finalBestSolutionConsumer} is called once with the final best solution, | |
* on that solver {@link Thread}, as soon as one is available. | |
* To listen to intermediate best solutions too, | |
* use {@link #solveAndListenToBestSolutions(Object, Object, Consumer, Consumer)} instead. | |
* <p> | |
* To stop a solver job before it naturally terminates, call {@link #terminateEarly(Object)}. | |
* @param problem never null, a {@link PlanningSolution}, usually its planning variables are uninitialized | |
* @param finalBestSolutionConsumer never null, called only once, on a solver thread | |
* @param exceptionHandler sometimes null, called if an exception or error occurs. | |
* If null it defaults to logging the exception as an error. | |
* @return a generated problem ID. Use that ID to {@link #terminateEarly(Object) terminate} the solver early | |
* or if {@link #addProblemFactChange(Object, ProblemFactChange) the problem changes in real-time}. | |
*/ | |
Object solve( | |
Solution_ problem, | |
Consumer<Solution_> finalBestSolutionConsumer, | |
Consumer<Throwable> exceptionHandler); | |
/** | |
* As defined by {@link #solve(Object, Consumer, Consumer)}, | |
* but instead of generating a unique problem ID, you need to supply one. | |
* @param problemId never null, a ID for each planning problem. This must be unique. | |
* @param problem never null, a {@link PlanningSolution}, usually its planning variables are uninitialized | |
* @param finalBestSolutionConsumer never null, called only once, on a solver thread | |
* @param exceptionHandler sometimes null, called if an exception or error occurs. | |
* If null it defaults to logging the exception as an error. | |
*/ | |
void solve(Object problemId, | |
Solution_ problem, | |
Consumer<Solution_> finalBestSolutionConsumer, | |
Consumer<Throwable> exceptionHandler); | |
/** | |
* Submits a planning problem to solve and returns immediately. | |
* The planning problem is solved on a solver {@link Thread}, as soon as one is available. | |
* <p> | |
* When the solver finds a new best solution, the {@code bestSolutionConsumer} is called every time, | |
* on a consumer {@link Thread}, as soon as it is available (taking into account any throttling waiting time), | |
* unless a newer best solution is already available by then (in which case skip ahead discards it). | |
* <p> | |
* To stop a solver job before it naturally terminates, call {@link #terminateEarly(Object)}. | |
* @param problem never null, a {@link PlanningSolution}, usually its planning variables are uninitialized | |
* @param bestSolutionConsumer never null, called multiple times, on a consumer thread | |
* @param exceptionHandler sometimes null, called if an exception or error occurs. | |
* If null it defaults to logging the exception as an error. | |
* @return a generated problem ID. Use that ID to {@link #terminateEarly(Object) terminate} the solver early | |
* or if {@link #addProblemFactChange(Object, ProblemFactChange) the problem changes in real-time}. | |
*/ | |
Object solveAndListenToBestSolutions( | |
Solution_ problem, | |
Consumer<Solution_> bestSolutionConsumer, | |
Consumer<Throwable> exceptionHandler); | |
/** | |
* As defined by {@link #solveAndListenToBestSolutions(Object, Consumer, Consumer)}, | |
* but instead of generating a unique problem ID, you need to supply one. | |
* @param problemId never null, a ID for each planning problem. This must be unique. | |
* @param problem never null, a {@link PlanningSolution}, usually its planning variables are uninitialized | |
* @param bestSolutionConsumer never null, called multiple times, on a consumer thread | |
* @param exceptionHandler sometimes null, called if an exception or error occurs. | |
* If null it defaults to logging the exception as an error. | |
*/ | |
void solveAndListenToBestSolutions( | |
Object problemId, | |
Solution_ problem, | |
Consumer<Solution_> bestSolutionConsumer, | |
Consumer<Throwable> exceptionHandler); | |
/** | |
* | |
* @param problemId never null, the return value from {@link #solve(Object, Consumer, Consumer)} | |
* or {@link #solveAndListenToBestSolutions(Object, Consumer, Consumer)} | |
*/ | |
void terminateEarly(Object problemId); | |
/** | |
* Submits a {@link ProblemFactChange} to apply on a solver. | |
* <p> | |
* If the solver has already terminated, this throws an exception. | |
* Put the solver in daemon mode to avoid that race condition. | |
* If the solver hasn't started yet, the {@link ProblemFactChange} runs before the solver starts. | |
* <p> | |
* As a side-effect, the {@link ProblemFactChange} restarts the {@link Solver}, | |
* effectively resetting all {@link Termination}s, but not {@link #terminateEarly(Object)}. | |
* @param problemId never null, the return value from {@link #solve(Object, Consumer, Consumer)} | |
* or {@link #solveAndListenToBestSolutions(Object, Consumer, Consumer)} | |
* @param problemFactChange never null | |
* @throws IllegalArgumentException if the problemId doesn't exist or if the solver has already terminated | |
*/ | |
void addProblemFactChange(Object problemId, ProblemFactChange<Solution_> problemFactChange); | |
/** | |
* Terminates all solvers, cancels all planning problems that haven't started yet | |
* and discards all queued {@link ProblemFactChange}s. | |
* Releases all thread pool resources. | |
* <p> | |
* No new planning problems can be submitted after calling this method. | |
*/ | |
@Override | |
void close(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment