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.