mvn clean org.openrewrite.maven:rewrite-maven-plugin:4.44.0:run -Drewrite.recipeArtifactCoordinates=org.optaplanner:optaplanner-migration:9.44.0.Final -Drewrite.activeRecipes=org.optaplanner.migration.ToLatest9
OptaPlanner’s public API classes are backwards compatible (per series), but users often also use impl classes (which are documented in the reference manual too). This upgrade recipe minimizes the pain to upgrade your code and to take advantage of the newest features in OptaPlanner.
Every upgrade note has an indication how likely your code will be affected by that change:
To upgrade from an older version, first apply the previous upgrade recipes. You will find the order of migration steps bellow:
The RHDM version differs from the OptaPlanner version:
RHDM version | OptaPlanner version |
---|---|
7.8 | 7.39 |
7.9 | 7.44 |
7.1 | 7.48 |
7.11 | 8.5 (and 7.52) |
7.12 | 8.11 (and 7.59) |
7.13 | 8.13 (and 7.67) |
Update your code in seconds, with optaplanner-migration
(an OpenRewrite recipe). Try it:
mvn clean org.openrewrite.maven:rewrite-maven-plugin:4.44.0:run -Drewrite.recipeArtifactCoordinates=org.optaplanner:optaplanner-migration:9.44.0.Final -Drewrite.activeRecipes=org.optaplanner.migration.ToLatest9
Note: The -Drewrite.recipeArtifactCoordinates might not work,
use the more verbose pom.xml
approach instead.
It only does upgrade steps with an Automated badge.
ScoreDefinition
: getLevelLabels()
addedIf you have a custom ScoreDefinition
: that interface has new method getLevelLabels()
.
AbstractScoreDefinition
and AbstractFeasibilityScoreDefinition
now expect those levelLabels
as a constructor parameter.
Before in *.java
:
public class HardSoftScoreDefinition extends AbstractFeasibilityScoreDefinition<HardSoftScore> {
public HardSoftScoreDefinition() {}
public int getLevelsSize() {
return 2;
}
...
}
After in *.java
:
public class HardSoftScoreDefinition extends AbstractFeasibilityScoreDefinition<HardSoftScore> {
public HardSoftScoreDefinition() {
super(new String[]{"hard score", "soft score"});
}
...
}
SolutionFileIO
: moved to another jarIf you’re using SolutionFileIO
: that class has been moved from the optaplanner-benchmark
jar to optaplanner-persistence-common
jar.
After in pom.xml
:
<dependency>
<groupId>org.optaplanner</groupId>
<artifactId>optaplanner-persistence-common</artifactId>
</dependency>
XStreamScoreConverter
: moved to another jarIf you’re using XStreamScoreConverter
: that class has been moved from the optaplanner-benchmark
jar to optaplanner-persistence-xstream
jar.
After in pom.xml
:
<dependency>
<groupId>org.optaplanner</groupId>
<artifactId>optaplanner-persistence-xstream</artifactId>
</dependency>
BendableScore
and XStream: XStreamBendableScoreConverter
replacedIf you’re using BendableScore
and XStream: XStreamBendableScoreConverter
(which only supported BendableScore
) has been deleted.
The generic XStreamScoreConverter
now supports BendableScore
, BendableLongScore
and BendableBigDecimalScore
too.
Before in *.java
:
@XStreamConverter(value = XStreamBendableScoreConverter.class, ints = {1, 2})
private BendableScore score;
After in *.java
:
@XStreamConverter(value = XStreamScoreConverter.class, types = {BendableScoreDefinition.class}, ints = {1, 2})
private BendableScore score;
ScoreDefinition
: fromLevelNumbers()
addedIf you have a custom ScoreDefinition
: that interface has another new method fromLevelNumbers()
.
It does the opposite of Score.toLevelNumbers()
.
After in *.java
:
public class HardSoftScoreDefinition extends AbstractFeasibilityScoreDefinition<HardSoftScore> {
...
@Override
public HardSoftScore fromLevelNumbers(Number[] levelNumbers) {
if (levelNumbers.length != getLevelsSize()) {
throw new IllegalStateException("The levelNumbers (" + Arrays.toString(levelNumbers)
+ ")'s length (" + levelNumbers.length + ") must equal the levelSize (" + getLevelsSize() + ").");
}
return HardSoftScore.valueOf((Integer) levelNumbers[0], (Integer) levelNumbers[1]);
}
}
If you’re using JPA-Hibernate with OptaPlanner, you’ll want to take advantage of the new jar optaplanner-persistence-jpa
.
See docs chapter "Integration" for more information.
SingleBenchmarkResult
methods renamedIf you’re using SingleBenchmarkResult
: the functionality of the method getScore()
and getUninitializedVariableCount()
has moved to
getAverageScore()
and getAverageUninitializedVariableCount()
because of the addition of SubSingleBenchmarkResult
(Statistical benchmarking).
Before in *.java
:
singleBenchmarkResult.getScore();
singleBenchmarkResult.getUninitializedVariableCount();
After in *.java
:
singleBenchmarkResult.getAverageScore();
singleBenchmarkResult.getAverageUninitializedVariableCount();
SingleStatistic
class: renamedIf you’re using SingleStatistic
or any of its subclasses: all of them were renamed to include "Sub" as a part of
Statistical benchmarking. The package org.optaplanner.benchmark.impl.statistic.single
was also renamed to
org.optaplanner.benchmark.impl.statistic.subsingle
. SingleStatisticType
has not been changed, therefore no changes
in the configuration are needed.
Before in *.java
:
import org.optaplanner.benchmark.impl.statistic.bestscore.BestScoreSingleStatistic;
...
BestScoreSingleStatistic singleStatistic;
After in *.java
:
import org.optaplanner.benchmark.impl.statistic.bestscore.BestScoreSubSingleStatistic;
...
BestScoreSubSingleStatistic subSingleStatistic;
SolverFactory
per request: use cloneSolverFactory()
If you’re configuring the SolverFactory
dynamically for each user request with differ settings,
use SolverFactory.cloneSolverFactory()
to avoid a race condition.
TerminationConfig
: clone()
removedThe method TerminationConfig.clone()
has been removed.
The inherit()
method now guarantees that afterwards changing the child or parent will not affect the other.
Before in *.java
:
TerminationConfig clone = terminationConfig.clone();
After in *.java
:
TerminationConfig clone = new TerminationConfig();
clone.inherit(terminationConfig);
SwingUtils
and SwingUncaughtExceptionHandler
: movedIf you’ve copy pasted the examples Swing UI, you might be using SwingUtils
and SwingUncaughtExceptionHandler
.
These classes are now in a different package (in the same jar for now).
Before in *.java
:
import org.optaplanner.benchmark.impl.aggregator.swingui.SwingUncaughtExceptionHandler;
import org.optaplanner.benchmark.impl.aggregator.swingui.SwingUtils;
After in *.java
:
import org.optaplanner.swing.impl.SwingUncaughtExceptionHandler;
import org.optaplanner.swing.impl.SwingUtils;
TangoColorFactory
: movedThe class TangoColorFactory
has moved to a different package and from optaplanner-examples into optaplanner-benchmark.
Before in *.java
:
import org.optaplanner.examples.common.swingui.TangoColorFactory;
After in *.java
:
import org.optaplanner.swing.impl.TangoColorFactory;
SolverFactory
and Solver
: added optional generic type parameterTo avoid the awkward cast to your Solution
implementation,
both SolverFactory
and Solver
now optionally support a generic type parameter.
Before in *.java
:
SolverFactory solverFactory = SolverFactory.createFromXmlResource(
"org/.../cloudBalancingSolverConfig.xml");
Solver solver = solverFactory.buildSolver();
solver.solve(unsolvedCloudBalance);
CloudBalance solvedCloudBalance = (CloudBalance) solver.getBestSolution();
After in *.java
:
SolverFactory<CloudBalance> solverFactory = SolverFactory.createFromXmlResource(
"org/.../cloudBalancingSolverConfig.xml");
Solver<CloudBalance> solver = solverFactory.buildSolver();
solver.solve(unsolvedCloudBalance);
CloudBalance solvedCloudBalance = solver.getBestSolution();
The old code still works too, because this is part of the public API which is backwards compatible.
Solver
: solve()
method returns the best solutionThe solve()
method now returns the best solution,
in which case calling getBestSolution()
is no longer needed:
Before in *.java
:
solver.solve(unsolvedCloudBalance);
CloudBalance solvedCloudBalance = solver.getBestSolution();
After in *.java
:
CloudBalance solvedCloudBalance = solver.solve(unsolvedCloudBalance);
The old code still works too, because this is part of the public API which is backwards compatible.
CustomPhaseCommand
: new method applyCustomProperties()
addedThe CustomPhaseCommand
has a new method applyCustomProperties()
to allow custom properties to be defined in the solver configuration.
Instead of implementing that method, you can also extend the new abstract class AbstractCustomPhaseCommand
which implements CustomPhaseCommand
and implements that method to do nothing (except a validation check).
Before in *.java
:
public class ToOriginalMachineSolutionInitializer implements CustomPhaseCommand {
...
}
After in *.java
:
public class ToOriginalMachineSolutionInitializer extends AbstractCustomPhaseCommand {
...
}
[.upgrade-recipe-readme] KieContainer and kjar support for Solver
OptaPlanner now supports kjars, to consume a kjar produced by OptaPlanner Workbench or to deploy a kjar to OptaPlanner Execution Server.
If you need to load a solver configuration or score rules that change more often than your application is released,
take a look at the reference manual and SolverFactory.createFromKieContainerXmlResource(…)
.
If the thread that called Solver.solve(…)
is interrupted,
the solver now terminates early.
This ensures a graceful shutdown of each solver when shutdownNow()
is called on an ExecutorService
of solvers.