Chapter 1. Changes that are not compatible with OptaPlanner 7.x or earlier

The changes listed in the section are not compatible with OptaPlanner 7.x or earlier versions of OptaPlanner.

Java 11 or higher required

Major, Public API

If you are using JRE or JDK 8, upgrade to JDK 11 or higher.

  • On Linux, get OpenJDK from your Linux software repository. On Fedora and Red Hat Enterprise Linux, enter the following command:

    sudo dnf install java-11-openjdk-devel
  • On Windows and macOS, download OpenJDK from the AdoptOpenJDK website.

SolverFactory and PlannerBenchmarkFactory no longer support KIE containers

Major, Public API

Because OptaPlanner now aligns with Kogito, the KIE container concept no longer applies. Therefore, SolverFactory no longer allows you to create Solver instances from KIE containers. This also applies to PlannerBenchmarkFactory and benchmarks.

OSGi metadata removed

Major, Public API

Because of the limited usage of OSGi and the maintenance burden it brings, the OptaPlanner JAR files in the OptaPlanner 8.x series no longer include OSGi metadata in their META-INF/MANIFEST.MF file.

Refrain from using Java serialization

Minor, Public API

In OptaPlanner 8, most uses of the Serializable marker interface were removed from the public API. Consider serializing with JSON or another format.

SolverFactory.getScoreDirectorFactory() replaced with ScoreManager

Major, Public API

In version 7 of OptaPlanner, using ScoreDirectorFactory was necessary in order to explain the score. In version 8 of OptaPlanner, new functionality was added to the ScoreManager and as a result there is no longer any reason to create new instances of ScoreDirector.

An example from a *.java file in OptaPlanner 7:

ScoreDirectorFactory<CloudBalance> scoreDirectorFactory = solverFactory.getScoreDirectorFactory();
try (ScoreDirector<CloudBalance> scoreDirector = scoreDirectorFactory.buildScoreDirector()) {
    scoreDirector.setWorkingSolution(solution);
    Score score = scoreDirector.calculateScore();
}

An example from a *.java file in OptaPlanner 8:

ScoreManager<CloudBalance> scoreManager = ScoreManager.create(solverFactory);
Score score = scoreManager.updateScore(solution);

Methods that allowed you to retrieve an instance of ScoreDirector and ScoreDirectorFactory have been removed from the public API without replacement. A reduced version of the ScoreDirector interface was promoted to the public API to promote the ProblemFactChange interface to the public API.

SolverFactory: getSolverConfig() removed

Minor, Public API

The SolverFactory.getSolverConfig() method has been deprecated and replaced with the SolverFactory.create(SolverConfig) method. A SolverConfig instance is now instantiated before a SolverFactory instance is instantiated, which is more natural. The previous order has been removed.

An example from a *.java file in OptaPlanner 7:

SolverFactory<MySolution> solverFactory = SolverFactory.createFromXmlResource(".../mySolverConfig.xml");
SolverConfig solverConfig = solverFactory.getSolverConfig();
...
Solver<MySolution> solver = solverFactory.buildSolver();

An example from a *.java file in OptaPlanner 8:

SolverConfig solverConfig = SolverConfig.createFromXmlResource(".../mySolverConfig.xml");
...
SolverFactory<MySolution> solverFactory = SolverFactory.create(solverConfig);
Solver<MySolution> solver = solverFactory.buildSolver();

If you were also passing a ClassLoader, pass it to both SolverConfig.createFromXmlResource() and SolverFactory.create().

SolverConfig: buildSolver() removed

Minor, Public API

The SolverConfig.buildSolver() method is an inner method that does not belong in the public API. Use the SolverFactory.buildSolver() method instead.

An example from a *.java file in OptaPlanner 7:

SolverConfig solverConfig = SolverConfig.createFromXmlResource(".../mySolverConfig.xml");
...
Solver<MySolution> solver = solverConfig.buildSolver();

An example from a *.java file in OptaPlanner 8:

SolverConfig solverConfig = SolverConfig.createFromXmlResource(".../mySolverConfig.xml");
...
SolverFactory<MySolution> solverFactory = SolverFactory.create(solverConfig);
Solver<MySolution> solver = solverFactory.buildSolver();

PlannerBenchmarkConfig: buildPlannerBenchmark() removed

Minor, Public API

The PlannerBenchmarkConfig.buildPlannerBenchmark() method is an inner method that does not belong in the public API. Use the PlannerBenchmarkFactory.buildPlannerBenchmark() method instead.

An example from a *.java file in OptaPlanner 7:

PlannerBenchmarkConfig benchmarkConfig = PlannerBenchmarkConfig.createFromXmlResource(
        ".../cloudBalancingBenchmarkConfig.xml");
...
PlannerBenchmark benchmark = benchmarkFactory.buildPlannerBenchmark();

An example from a *.java file in OptaPlanner 8:

PlannerBenchmarkConfig benchmarkConfig = PlannerBenchmarkConfig.createFromXmlResource(
        ".../cloudBalancingBenchmarkConfig.xml");
...
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.create(benchmarkConfig);
PlannerBenchmark benchmark = benchmarkFactory.buildPlannerBenchmark();

SolverFactory: cloneSolverFactory() removed

Minor, Public API

The SolverFactory.cloneSolverFactory() method has been deprecated and replaced with the new SolverConfig(SolverConfig) copy constructors and the SolverFactory.cloneSolverFactory() method has been removed.

An example from a *.java file in OptaPlanner 7:

private SolverFactory<MySolution> base;

public void userRequest(..., long userInput) {
    SolverFactory<MySolution> solverFactory = base.cloneSolverFactory();
    solverFactory.getSolverConfig()
            .getTerminationConfig()
            .setMinutesSpentLimit(userInput);
    Solver<MySolution> solver = solverFactory.buildSolver();
    ...
}

An example from a *.java file in OptaPlanner 8:

private SolverConfig base;

public void userRequest(..., long userInput) {
    SolverConfig solverConfig = new SolverConfig(base); // Copy it
    solverConfig.getTerminationConfig()
            .setMinutesSpentLimit(userInput);
    SolverFactory<MySolution> solverFactory = SolverFactory.create(solverConfig);
    Solver<MySolution> solver = solverFactory.buildSolver();
    ...
}

SolverFactory: createEmpty() removed

Minor, Public API

The SolverFactory.createEmpty() method has been deprecated and replaced with the new SolverConfig() method. The SolverFactory.createEmpty() method has been removed.

An example from a *.java file in OptaPlanner 7:

SolverFactory<MySolution> solverFactory = SolverFactory.createEmpty();
SolverConfig solverConfig = solverFactory.getSolverConfig()
...
Solver<MySolution> solver = solverFactory.buildSolver();

An example from a *.java file in OptaPlanner 8:

SolverConfig solverConfig = new SolverConfig();
...
SolverFactory<MySolution> solverFactory = SolverFactory.create(solverConfig);
Solver<MySolution> solver = solverFactory.buildSolver();

Major, Public API

OptaPlanner now provides an XML schema definition for the solver configuration. Although OptaPlanner retains compatibility with previous versions of the existing XML configuration, migrating to the XSD is strongly recommended because OptaPlanner might support only valid configuration XML in the future.

An example from the *SolverConfig.xml file in OptaPlanner 7:

<?xml version="1.0" encoding="UTF-8"?>
<solver>
  ...
</solver>

An example from the *SolverConfig.xml file in OptaPlanner 8:

<?xml version="1.0" encoding="UTF-8"?>
<solver xmlns="https://www.optaplanner.org/xsd/solver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://www.optaplanner.org/xsd/solver https://www.optaplanner.org/xsd/solver/solver.xsd">
  ...
</solver>

Using the XSD might require reordering some of the XML elements of the configuration. Use code completion in the IDE to migrate to a valid XML.

Property subPillarEnabled in move selector configuration has been removed

Minor, Public API

The subPillarEnabled property on PillarSwapMoveSelector and PillarChangeMoveSelector has been deprecated and replaced with a new property, subPillarType. The subPillarEnabled property has been removed.

An example from the *SolverConfig.xml and *BenchmarkConfig.xml files in OptaPlanner 7:

      <pillar...MoveSelector>
        ...
        <pillarSelector>
          <subPillarEnabled>false</subPillarEnabled>
          ...
        </pillarSelector>
        ...
      </pillar...MoveSelector>

An example from the *SolverConfig.xml and *BenchmarkConfig.xml files in OptaPlanner 8:

      <pillar...MoveSelector>
        <subPillarType>NONE</subPillarType>
        <pillarSelector>
          ...
        </pillarSelector>
        ...
      </pillar...MoveSelector>

Solver: getScoreDirectorFactory() removed

Major, Public API

The getScoreDirectorFactory() method has been deprecated and has now been removed from both Solver and SolverFactory classes.

You no longer need to create a Solver instance just to calculate or explain a score in the UI. Use the ScoreManager API instead.

An example from a *.java file in OptaPlanner 7:

SolverFactory<VehicleRoutingSolution> solverFactory = SolverFactory.createFromXmlResource(...);
Solver<VehicleRoutingSolution> solver = solverFactory.buildSolver();
uiScoreDirectorFactory = solver.getScoreDirectorFactory();
...

An example from a *.java file in OptaPlanner 8:

SolverFactory<VehicleRoutingSolution> solverFactory = SolverFactory.createFromXmlResource(...);
ScoreManager<VehicleRoutingSolution> scoreManager = ScoreManager.create(solverFactory);
...

ScoreDirectorFactory should not be used anymore because it has always been outside the public API and all of its functionality is exposed in various parts of the public API.

Solver.explainBestScore() has been removed

Major, Public API

The explainBestScore() method on the Solver interface was deprecated in 7.x and has now been removed. You can obtain the same information through the new ScoreManager API.

Red Hat recommends that you do not parse the results of this method call in any way.

An example from a *.java file in OptaPlanner 7:

solver = ...;
scoreExplanation = solver.explainBestScore();

An example from a *.java file in OptaPlanner 8:

MySolution solution = ...;
ScoreManager<MySolution> scoreManager = ...;
scoreExplanation = scoreManager.explainScore(solution);

The Solver interface methods getBestSolution(), getBestScore(), and getTimeMillisSpent() have been removed

Minor, Public API

Several methods on the Solver interface were deprecated in 7.x and have been removed. You can obtain the same information by registering an EventListener through the Solver.addEventListener(…​).

An example from a *.java file in OptaPlanner 7:

solver = ...;
solution = solver.getBestSolution();
score = solver.getBestScore();
timeMillisSpent = solver.getTimeMillisSpent();

An example from a *.java file in OptaPlanner 8:

solver = ...;
solver.addEventListener(event -> {
    solution = event.getNewBestSolution();
    score = event.getNewBestScore();
    timeMillisSpent = event.getTimeMillisSpent();
});

Annotation scanning has been removed

Major, Public API

The <scanAnnotatedClasses/> directive in the solver configuration was deprecated in 7.x and is now removed.

An example from the *.xml file in OptaPlanner 7:

<solver>
    ...
    <scanAnnotatedClasses/>
    ...
</solver>

An example from the *.xml file in OptaPlanner 8:

<solver>
    ...
    <solutionClass>...</solutionClass>
    <entityClass>...</entityClass>
    ...
</solver>

New package for @PlanningFactProperty and @PlanningFactCollectionProperty

Major, Public API

The @PlanningFactProperty and @PlanningFactCollectionProperty annotations now share the same package with other similar annotations, such as @PlanningSolution. The old annotations were deprecated in 7.x and removed.

An example from a *.java file in OptaPlanner 7:

import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.solution.drools.ProblemFactProperty;

An example from a *.java file in OptaPlanner 8:

import org.optaplanner.core.api.domain.solution.ProblemFactCollectionProperty;
import org.optaplanner.core.api.domain.solution.ProblemFactProperty;

filterClassList replaced with a single filter class

Minor, Public API

The configuration of EntitySelector, ValueSelector, and MoveSelector now has a single filter class in both the configuration API and the solver configuration XML.

In practice, you do not need multiple selection filter classes often, and you can replace them with a single selection filter class that implements the logic of all of them. Passing a single selection class now requires less boilerplate code.

An example from a *.java file in OptaPlanner 7:

ValueSelectorConfig valueSelectorConfig = new ValueSelectorConfig();
valueSelectorConfig.setFilterClassList(Collections.singletonList(MySelectionFilterClass.class));

An example from a *.java file in OptaPlanner 8:

ValueSelectorConfig valueSelectorConfig = new ValueSelectorConfig();
valueSelectorConfig.setFilterClass(MySelectionFilterClass.class);

Replacing multiple selection filter classes with a single selection filter class

An example from the *.xml file in OptaPlanner 7:

<swapMoveSelector>
  <entitySelector>
    <filterClass>com.example.FilterA</filterClass>
    <filterClass>com.example.FilterB</filterClass>
  </entitySelector>
</swapMoveSelector>

An example from a *.java file in OptaPlanner 7:

package com.example;
...
public class FilterA implements SelectionFilter<MySolution, MyPlanningEntity> {

    @Override
    public boolean accept(ScoreDirector<MySolution> scoreDirector, MyPlanningEntity selection) {
        return selection.getValue() < 500;
    }
}
package com.example;
...
public class FilterB implements SelectionFilter<MySolution, MyPlanningEntity> {

    @Override
    public boolean accept(ScoreDirector<MySolution> scoreDirector, MyPlanningEntity selection) {
        return selection.getOrder() == Order.ASC;
    }
}

An example from the *.xml file in OptaPlanner 8:

<swapMoveSelector>
  <entitySelector>
    <filterClass>com.example.SingleEntityFilter</filterClass>
  </entitySelector>
</swapMoveSelector>

An example from a *.java file in OptaPlanner 8:

package com.example;
...
public class SingleEntityFilter implements SelectionFilter<MySolution, MyPlanningEntity> {

    @Override
    public boolean accept(ScoreDirector<MySolution> scoreDirector, MyPlanningEntity selection) {
        return selection.getValue() < 500 && selection.getOrder() == Order.ASC;
    }
}

AcceptorConfig renamed to LocalSearchAcceptorConfig

Minor

This only impacts the configuration API. The solver configuration XML file remains intact.

Naming consistency with other local-search-specific configuration classes has been implemented.

An example from a *.java file in OptaPlanner 7:

LocalSearchPhaseConfig localSearchPhaseConfig = new LocalSearchPhaseConfig()
        .withAcceptorConfig(new AcceptorConfig().withEntityTabuSize(5));

An example from a *.java file in OptaPlanner 8:

LocalSearchPhaseConfig localSearchPhaseConfig = new LocalSearchPhaseConfig()
        .withAcceptorConfig(new LocalSearchAcceptorConfig().withEntityTabuSize(5));

Custom properties XML configuration format changes

Minor, Public API

This issue only impacts the solver configuration XML, specifically <scoreDirectorFactory/>, <moveIteratorFactory/>, <moveListFactory/>, <partitionedSearch/> and <customPhase/>.

This change was made to enforce the structure of the configuration XML in build time.

An example from the *.xml file in OptaPlanner 7:

<partitionedSearch>
  <solutionPartitionerClass>com.example.MySolutionPartitioner</solutionPartitionerClass>
  <solutionPartitionerCustomProperties>
    <partCount>4</partCount> <!-- a custom property -->
    <minimumProcessListSize>300</minimumProcessListSize> <!-- a custom property -->
  </solutionPartitionerCustomProperties>
</partitionedSearch>

An example from the *.xml file in OptaPlanner 8:

<partitionedSearch>
  <solutionPartitionerClass>com.example.MySolutionPartitioner</solutionPartitionerClass>
  <solutionPartitionerCustomProperties>
    <property name="partCount" value="4"/> <!-- a custom property -->
    <property name="minimumProcessListSize" value="300"/> <!-- a custom property -->
  </solutionPartitionerCustomProperties>
</partitionedSearch>

<variableNameInclude/> elements are now wrapped by the <variableNameIncludes/> element

Minor, Public API

This update only impacts the solver configuration XML, specifically the <swapMoveSelector/> and <pillarSwapMoveSelector/>.

This change was made to enforce the structure of the configuration XML in build time.

An example from the *.xml file in OptaPlanner 7:

<swapMoveSelector>
  <variableNameInclude>variableA</variableNameInclude>
  <variableNameInclude>variableB</variableNameInclude>
</swapMoveSelector>

An example from the *.xml file in OptaPlanner 8:

<swapMoveSelector>
  <variableNameIncludes>
    <variableNameInclude>variableA</variableNameInclude>
    <variableNameInclude>variableB</variableNameInclude>
  </variableNameIncludes>
</swapMoveSelector>

Solution interface removed

Minor, Public API

The Solution interface was deprecated and removed. The AbstractSolution interface which is only used by Business Central has also been removed.

Remove the Solution interface, annotate the getScore() method with @PlanningScore, and replace the getProblemFacts() method with a @ProblemFactCollectionProperty annotation directly on every problem fact getter (or field).

An example from a *.java file in OptaPlanner 7:

@PlanningSolution
public class CloudBalance implements Solution<HardSoftScore> {

    private List<CloudComputer> computerList;
    ...

    private HardSoftScore score;

    @ValueRangeProvider(id = "computerRange")
    public List<CloudComputer> getComputerList() {...}

    public HardSoftScore getScore() {...}
    public void setScore(HardSoftScore score) {...}

    public Collection<? extends Object> getProblemFacts() {
        List<Object> facts = new ArrayList<Object>();
        facts.addAll(computerList);
        ...
        return facts;
    }

}

An example from a *.java file in OptaPlanner 8:

@PlanningSolution
public class CloudBalance {

    private List<CloudComputer> computerList;
    ...

    private HardSoftScore score;

    @ValueRangeProvider(id = "computerRange")
    @ProblemFactCollectionProperty
    public List<CloudComputer> getComputerList() {...}

    @PlanningScore
    public HardSoftScore getScore() {...}
    public void setScore(HardSoftScore score) {...}

}

For a single problem fact that is not wrapped in a Collection, use the @ProblemFactProperty annotation, as shown in the following example, with field annotations this time:

An example from a *.java file in OptaPlanner 7:

@PlanningSolution
public class CloudBalance implements Solution<HardSoftScore> {

    private CloudParametrization parametrization;
    private List<CloudBuilding> buildingList;
    @ValueRangeProvider(id = "computerRange")
    private List<CloudComputer> computerList;
    ...

    public Collection<? extends Object> getProblemFacts() {
        List<Object> facts = new ArrayList<Object>();
        facts.add(parametrization); // not a Collection
        facts.addAll(buildingList);
        facts.addAll(computerList);
        ...
        return facts;
    }

}

An example from a *.java file in OptaPlanner 8:

@PlanningSolution
public class CloudBalance {

    @ProblemFactProperty
    private CloudParametrization parametrization;
    @ProblemFactCollectionProperty
    private List<CloudBuilding> buildingList;
    @ValueRangeProvider(id = "computerRange")
    @ProblemFactCollectionProperty
    private List<CloudComputer> computerList;
    ...

}

Do not add the @ProblemFactCollectionProperty annotation on getters (or fields) that have a @PlanningEntityCollectionProperty annotation.

BestSolutionChangedEvent: isNewBestSolutionInitialized() removed

Minor, Public API

The BestSolutionChangedEvent.isNewBestSolutionInitialized() method has been deprecated and replaced with the BestSolutionChangedEvent.getNewBestSolution().getScore().isSolutionInitialized() method. The BestSolutionChangedEvent.isNewBestSolutionInitialized() method has been removed.

An example from a *.java file in OptaPlanner 7:

    public void bestSolutionChanged(BestSolutionChangedEvent<CloudBalance> event) {
        if (event.isEveryProblemFactChangeProcessed()
                && event.isNewBestSolutionInitialized()) {
            ...
        }
    }

An example from a *.java file in OptaPlanner 8:

    public void bestSolutionChanged(BestSolutionChangedEvent<CloudBalance> event) {
        if (event.isEveryProblemFactChangeProcessed()
                && event.getNewBestSolution().getScore().isSolutionInitialized()) {
            ...
        }
    }

If you check isFeasible(), it checks if the solution is initialized.

An example from a *.java file in OptaPlanner 8:

    public void bestSolutionChanged(BestSolutionChangedEvent<CloudBalance> event) {
        if (event.isEveryProblemFactChangeProcessed()
                // isFeasible() checks isSolutionInitialized() too
                && event.getNewBestSolution().getScore().isFeasible()) {
            ...
        }
    }

<valueSelector>: variableName is now an attribute

Minor, Public API

When power-tweaking move selectors, such as <changeMoveSelector>, in a use case with multiple planning variables, the <variableName> XML element has been replaced with a variableName="…​" XML attribute. This change reduces the solver configuration verbosity. After being deprecated for the entire 7.x series, the old way has now been removed.

An example from the *SolverConfig.xml and *BenchmarkConfig.xml files in OptaPlanner 7:

  <valueSelector>
    <variableName>room</variableName>
  </valueSelector>

An example from the *SolverConfig.xml and *BenchmarkConfig.xml files in OptaPlanner 8:

  <valueSelector variableName="room"/>

Partitioned Search: threadFactoryClass removed

Minor, Public API

Because <solver> has supported a <threadFactoryClass> element for some time, the <threadFactoryClass> element under <partitionedSearch> has been removed.

An example from the *SolverConfig.xml and *BenchmarkConfig.xml files in OptaPlanner 7:

  <solver>
    ...
    <partitionedSearch>
      <threadFactoryClass>...MyAppServerThreadFactory</threadFactoryClass>
      ...
    </partitionedSearch>
  </solver>

An example from the *SolverConfig.xml and *BenchmarkConfig.xml files in OptaPlanner 8:

  <solver>
    <threadFactoryClass>...MyAppServerThreadFactory</threadFactoryClass>
    ...
    <partitionedSearch>
      ...
    </partitionedSearch>
  </solver>

SimpleDoubleScore and HardSoftDoubleScore removed

Minor, Public API

The use of double-based score types is not recommended because they can cause score corruption. They have been removed.

An example from a *.java file in OptaPlanner 7:

@PlanningSolution
public class MyPlanningSolution {

    private SimpleDoubleScore score;

    ...

}

An example from a *.java file in OptaPlanner 8:

@PlanningSolution
public class MyPlanningSolution {

    private SimpleLongScore score;

    ...

}

Score.toInitializedScore() removed

Minor, Public API

The Score.toInitializedScore() method was deprecated and replaced with the Score.withInitScore(int) method in 7.x and is now removed.

An example from a *.java file in OptaPlanner 7:

score = score.toInitializedScore();

An example from a *.java file in OptaPlanner 8:

score = score.withInitScore(0);

Various justification Comparators removed

Minor, Public API

The following Comparator implementations were deprecated in 7.x and now removed:

  • org.optaplanner.core.api.score.comparator.NaturalScoreComparator
  • org.optaplanner.core.api.score.constraint.ConstraintMatchScoreComparator
  • org.optaplanner.core.api.score.constraint.ConstraintMatchTotalScoreComparator
  • org.optaplanner.core.api.score.constraint.IndictmentScoreComparator

An example from a *.java file in OptaPlanner 7:

NaturalScoreComparator comparator = new NaturalScoreComparator();
ConstraintMatchScoreComparator comparator2 = new ConstraintMatchScoreComparator();

An example from a *.java file in OptaPlanner 8:

Comparator<Score> comparator = Comparable::compareTo;
Comparator<ConstraintMatch> comparator2 = Comparator.comparing(ConstraintMatch::getScore);

FeasibilityScore removed

Minor, Public API

The FeasibilityScore interface was deprecated in 7.x and its only method isFeasible() moved to the Score supertype. The interface has now been removed.

You should refer to Scores by their ultimate type, for example HardSoftScore instead of to Score.

@PlanningEntity.movableEntitySelectionFilter removed

Minor, Public API

The movableEntitySelectionFilter field on the @PlanningEntity annotation was deprecated in 7.x and a new field pinningFilter has been introduced with a name that shows the relation to the @PlanningPin annotation. This filter implements a new PinningFilter interface, returning true if the entity is pinned, and false if movable. The logic of this new filter is therefore inverted as compared to the old filter.

You should update your @PlanningEntity annotations by supplying the new filter instead of the old filter. The old filter has now been removed.

An example from a *.java file in OptaPlanner 7:

@PlanningEntity(movableEntitySelectionFilter = MyMovableEntitySelectionFilter.class)

An example from a *.java file in OptaPlanner 8:

@PlanningEntity(pinningFilter = MyPinningFilter.class)

@PlanningVariable.reinitializeVariableEntityFilter removed

Minor, Public API

The reinitializeVariableEntityFilter field on the @PlanningVariable annotation was deprecated in 7.x and now removed.

*ScoreHolder classes turned into interfaces

Minor, Public API

In OptaPlanner 7, ScoreHolder classes, used exclusively for Drools score calculation, exposed a number of public methods which, if used, allowed the user to unintentionally corrupt or otherwise negatively affect their scores.

In OptaPlanner 8, these methods have been removed and the classes have been turned into interfaces. Most users do not use the removed and potentially harmful methods.

However, if you do use these methods, you will find suitable replacements in the public API in areas of score explanation and constraint configuration.

ValueRangeFactory class now final

Minor

ValueRangeFactory class is a factory class that has only static methods. There is no need for you to extend this class, and it has therefore been made final.

An example from a *.java file in OptaPlanner 7:

class MyValueRangeFactory extends ValueRangeFactory {
    ...
}

An example from a *.java file in OptaPlanner 8:

class MyValueRangeFactory {
    ...
}

ConstraintMatchTotal and Indictment are now interfaces

Minor, Public API

ConstraintMatchTotal and Indictment classes have been converted into interfaces. As a result, their implementations were moved out of the public API, together with methods that allowed them to mutate their state. These methods were never intended for the public API, and therefore there is no replacement for them.

You might still need the instances themselves if you choose to implement ConstraintMatchAwareIncrementalScoreCalculator:

ConstraintMatchTotal maximumCapacityMatchTotal = new ConstraintMatchTotal(...);

An example from a *.java file in OptaPlanner 8:

ConstraintMatchTotal maximumCapacityMatchTotal = new DefaultConstraintMatchTotal(...);

ScoreManager: generic type Score added

Major, Public API

The ScoreManager and ScoreExplanation APIs now have the generic type Score to avoid downcasts in your code, for example from Score to HardSoftScore.

An example from a *.java file in OptaPlanner 7:

    @Inject // or @Autowired
    ScoreManager<TimeTable> scoreManager;

An example from a *.java file in OptaPlanner 8:

    @Inject // or @Autowired
    ScoreManager<TimeTable, HardSoftScore> scoreManager;

An example from a *.java file in OptaPlanner 7:

    ScoreExplanation<TimeTable> explanation = scoreManager.explainScore(timeTable);
    HardSoftScore score = (HardSoftScore) explanation.getScore();

An example from a *.java file in OptaPlanner 8:

    ScoreExplanation<TimeTable, HardSoftScore> explanation = scoreManager.explainScore(timeTable);
    HardSoftScore score = explanation.getScore();

ConstraintMatchTotal, ConstraintMatch, and Indictment: generic type Score added

Major

Similar to ScoreManager and ScoreExplanation, the ConstraintMatchTotal, ConstraintMatch, and Indictment APIs now have a generic type Score to avoid downcasts in your code, for example from Score to HardSoftScore.

An example from a *.java file in OptaPlanner 7:

    ScoreExplanation<TimeTable> explanation = scoreManager.explainScore(timeTable);
    Map<String, ConstraintMatchTotal> constraintMatchTotalMap = scoreExplanation.getConstraintMatchTotalMap();
    ConstraintMatchTotal constraintMatchTotal = constraintMatchTotalMap.get(contraintId);
    HardSoftScore totalScore = (HardSoftScore) constraintMatchTotal.getScore();

An example from a *.java file in OptaPlanner 8:

    ScoreExplanation<TimeTable, HardSoftScore> explanation = scoreManager.explainScore(timeTable);
    Map<String, ConstraintMatchTotal<HardSoftScore>> constraintMatchTotalMap = scoreExplanation.getConstraintMatchTotalMap();
    ConstraintMatchTotal<HardSoftScore> constraintMatchTotal = constraintMatchTotalMap.get(contraintId);
    HardSoftScore totalScore = constraintMatchTotal.getScore();

An example from a *.java file in OptaPlanner 7:

    ScoreExplanation<TimeTable> explanation = scoreManager.explainScore(timeTable);
    Map<Object, Indictment> indictmentMap = scoreExplanation.getIndictmentMap();
    Indictment indictment = indictmentMap.get(lesson);
    HardSoftScore totalScore = (HardSoftScore) indictment.getScore();

An example from a *.java file in OptaPlanner 8:

    ScoreExplanation<TimeTable, HardSoftScore> explanation = scoreManager.explainScore(timeTable);
    Map<Object, Indictment<HardSoftScore>> indictmentMap = scoreExplanation.getIndictmentMap();
    Indictment<HardSoftScore> indictment = indictmentMap.get(lesson);
    HardSoftScore totalScore = indictment.getScore();

ConstraintMatchAwareIncrementalScoreCalculator: generic type Score added

Minor

The interface ConstraintMatchAwareIncrementalScoreCalculator now also has a generic type parameter for Score to avoid raw type usages of ConstraintMatchTotal and Indictment.

An example from a *.java file in OptaPlanner 7:

public class MachineReassignmentIncrementalScoreCalculator
        implements ConstraintMatchAwareIncrementalScoreCalculator<MachineReassignment> {

    @Override
    public Collection<ConstraintMatchTotal> getConstraintMatchTotals() {
        ...
    }


    @Override
    public Map<Object, Indictment> getIndictmentMap() {
        ...
    }

}

An example from a *.java file in OptaPlanner 8:

public class MachineReassignmentIncrementalScoreCalculator
        implements ConstraintMatchAwareIncrementalScoreCalculator<MachineReassignment, HardSoftLongScore> {

    @Override
    public Collection<ConstraintMatchTotal<HardSoftLongScore>> getConstraintMatchTotals() {
        ...
    }


    @Override
    public Map<Object, Indictment<HardSoftLongScore>> getIndictmentMap() {
        ...
    }

}

AbstractCustomPhaseCommand was removed

Minor, Public API

The abstract class AbstractCustomPhaseCommand was removed. Any class that extends it should directly implement the CustomPhaseCommand interface.

An example from a *.java file in OptaPlanner 7:

public class DinnerPartySolutionInitializer extends AbstractCustomPhaseCommand<DinnerParty> {

    @Override
    public void changeWorkingSolution(ScoreDirector<DinnerParty> scoreDirector) {
        ...
    }

}

An example from a *.java file in OptaPlanner 8:

public class DinnerPartySolutionInitializer implements CustomPhaseCommand<DinnerParty> {

    @Override
    public void changeWorkingSolution(ScoreDirector<DinnerParty> scoreDirector) {
        ...
    }

}

Score calculators moved to the public API

Major

The interfaces EasyScoreCalculator, IncrementalScoreCalculator, and ConstraintMatchAwareIncrementalScoreCalculator have moved to a new package in the public API. Their deprecated counterparts have been removed. The deprecated class org.optaplanner.core.impl.score.director.incremental.AbstractIncrementalScoreCalculator has also been removed. Replace the use of the removed interfaces and classes with their counterparts in the public API.

An example from the EasyScoreCalculator.java file in OptaPlanner 7:

  ...
  import org.optaplanner.core.impl.score.director.easy.EasyScoreCalculator;
  ...

  public class CloudBalancingEasyScoreCalculator implements EasyScoreCalculator<CloudBalance> {
    ...
  }

An example from the EasyScoreCalculator.java file in OptaPlanner 8:

  ...
  import org.optaplanner.core.api.score.calculator.EasyScoreCalculator;
  ...

  public class CloudBalancingEasyScoreCalculator implements EasyScoreCalculator<CloudBalance, HardSoftScore> {
    ...
  }

An example from the IncrementalScoreCalculator.java file in OptaPlanner 7:

  ...
  import org.optaplanner.core.impl.score.director.incremental.AbstractIncrementalScoreCalculator;
  ...

  public class CloudBalancingIncrementalScoreCalculator extends AbstractIncrementalScoreCalculator<CloudBalance> {
    ...
  }

An example from the IncrementalScoreCalculator.java file in OptaPlanner 8:

  ...
  import org.optaplanner.core.api.score.calculator.IncrementalScoreCalculator;
  ...

  public class CloudBalancingIncrementalScoreCalculator implements IncrementalScoreCalculator<CloudBalance, HardSoftScore> {
    ...
  }

An example from the ConstraintMatchAwareIncrementalScoreCalculator.java file in OptaPlanner 7:

  ...
  import org.optaplanner.core.impl.score.director.incremental.AbstractIncrementalScoreCalculator;
  import org.optaplanner.core.impl.score.director.incremental.ConstraintMatchAwareIncrementalScoreCalculator;
  ...

  public class CheapTimeConstraintMatchAwareIncrementalScoreCalculator
        extends AbstractIncrementalScoreCalculator<CheapTimeSolution>
        implements ConstraintMatchAwareIncrementalScoreCalculator<CheapTimeSolution> {
    ...
  }

An example from the ConstraintMatchAwareIncrementalScoreCalculator.java file in OptaPlanner 8:

  ...
  import org.optaplanner.core.api.score.calculator.ConstraintMatchAwareIncrementalScoreCalculator;
  ...

  public class CheapTimeConstraintMatchAwareIncrementalScoreCalculator
        implements ConstraintMatchAwareIncrementalScoreCalculator<CheapTimeSolution, HardMediumSoftLongScore> {
    ...
  }

PlannerBenchmarkFactory: createFromSolverFactory() removed

Major, Public API

The PlannerBenchmarkFactory.createFromSolverFactory() method has been deprecated and replaced with the PlannerBenchmarkFactory.createFromSolverConfigXmlResource(String) method. The PlannerBenchmarkFactory.createFromSolverFactory() method has been removed.

An example from a *.java file in OptaPlanner 7:

SolverFactory<CloudBalance> solverFactory = SolverFactory.createFromXmlResource(
        ".../cloudBalancingSolverConfig.xml");
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.createFromSolverFactory(solverFactory);

An example from a *.java file in OptaPlanner 8:

PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.createFromSolverConfigXmlResource(
        ".../cloudBalancingSolverConfig.xml");

If you programmatically adjust the solver configuration, you can use PlannerBenchmarkConfig.createFromSolverConfig(SolverConfig) and then PlannerBenchmarkFactory.create(PlannerBenchmarkConfig) instead.

PlannerBenchmarkFactory: getPlannerBenchmarkConfig() removed

Minor, Public API

The PlannerBenchmarkFactory.getPlannerBenchmarkConfig() method has been deprecated and replaced with the PlannerBenchmarkFactory.create(PlannerBenchmarkConfig) method. A PlannerBenchmarkConfig instance is now instantiated before a PlannerBenchmarkFactory instance is instantiated. This order is more logical. PlannerBenchmarkFactory.getPlannerBenchmarkConfig() has been removed.

An example from a *.java file in OptaPlanner 7:

PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.createFromXmlResource(
        ".../cloudBalancingBenchmarkConfig.xml");
PlannerBenchmarkConfig benchmarkConfig = benchmarkFactory.getPlannerBenchmarkConfig();
...
PlannerBenchmark benchmark = benchmarkFactory.buildPlannerBenchmark();

An example from a *.java file in OptaPlanner 8:

PlannerBenchmarkConfig benchmarkConfig = PlannerBenchmarkConfig.createFromXmlResource(
        ".../cloudBalancingBenchmarkConfig.xml");
...
PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.create(benchmarkConfig);
PlannerBenchmark benchmark = benchmarkFactory.buildPlannerBenchmark();

Minor, Public API

OptaPlanner now provides an XML Schema Definition (XSD) for the benchmark configuration. Although OptaPlanner keeps compatibility with earlier versions of the existing XML configuration, migrating to the XSD is strongly recommended because OptaPlanner might support only valid configuration XML in the future.

An example from the *BenchmarkConfig.xml file in OptaPlanner 7:

<?xml version="1.0" encoding="UTF-8"?>
<plannerBenchmark>
  ...
</plannerBenchmark>

An example from the *BenchmarkConfig.xml file in OptaPlanner 8:

<?xml version="1.0" encoding="UTF-8"?>
<plannerBenchmark xmlns="https://www.optaplanner.org/xsd/benchmark" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://www.optaplanner.org/xsd/benchmark https://www.optaplanner.org/xsd/benchmark/benchmark.xsd">
  ...
</plannerBenchmark>

Using the XSD might require reordering some of the XML elements of the configuration. Use code completion in the IDE to migrate to a valid XML.

ProblemBenchmarksConfig: xStreamAnnotatedClass removed

Major, Public API

The <xStreamAnnotatedClass/> has been removed from the <problemBenchmarks/> configuration together with the corresponding getXStreamAnnotatedClassList() and setXStreamAnnotatedClassList() methods in the ProblemBenchmarksConfig class.

An example from a *.java file in OptaPlanner 7:

ProblemBenchmarksConfig problemBenchmarksConfig = new ProblemBenchmarksConfig();
problemBenchmarksConfig.setXStreamAnnotatedClassList(MySolution.class);

An example from a *.java file in OptaPlanner 8:

package com.example;
...
public class MySolutionFileIO extends XStreamSolutionFileIO<MySolution> {
    public MySolutionFileIO() {
        super(MySolution.class);
    }
}

...

ProblemBenchmarksConfig problemBenchmarksConfig = new ProblemBenchmarksConfig();
problemBenchmarksConfig.setSolutionFileIOClass(MySolutionFileIO.class);

An example from the *BenchmarkConfig.xml file in OptaPlanner 7:

<plannerBenchmark>
...
  <solverBenchmark>
    <problemBenchmarks>
      <xStreamAnnotatedClass>com.example.MySolution</xStreamAnnotatedClass>
      ...
    </problemBenchmarks>
    ...
  </solverBenchmark>
...
</plannerBenchmark>

An example from the *BenchmarkConfig.xml file in OptaPlanner 8:

<plannerBenchmark>
...
  <solverBenchmark>
    <problemBenchmarks>
      <!-- See the "After in *.java" section to create the MySolutionFileIO. -->
      <solutionFileIOClass>com.example.MySolutionFileIO</solutionFileIOClass>
      ...
    </problemBenchmarks>
    ...
  </solverBenchmark>
...
</plannerBenchmark>

BenchmarkAggregatorFrame: createAndDisplay(PlannerBenchmarkFactory) removed

Minor

The BenchmarkAggregatorFrame.createAndDisplay(PlannerBenchmarkFactory) method has been deprecated and replaced with the BenchmarkAggregatorFrame.createAndDisplayFromXmlResource(String) method. The BenchmarkAggregatorFrame.createAndDisplay(PlannerBenchmarkFactory) method has been removed.

An example from a *.java file in OptaPlanner 7:

PlannerBenchmarkFactory benchmarkFactory = PlannerBenchmarkFactory.createFromXmlResource(
        ".../cloudBalancingBenchmarkConfig.xml");
BenchmarkAggregatorFrame.createAndDisplay(benchmarkFactory);

An example from a *.java file in OptaPlanner 8:

BenchmarkAggregatorFrame.createAndDisplayFromXmlResource(
        ".../cloudBalancingBenchmarkConfig.xml");

If you programmatically adjust the benchmark configuration, you can use BenchmarkAggregatorFrame.createAndDisplay(PlannerBenchmarkConfig) instead.

Removed JavaScript expression support in configuration

Minor

Various elements of both the solver configuration and benchmark configuration no longer support nested JavaScript expressions. You must replace these with either auto-configuration or with integer constants.

An example from the solverConfig.xml file in OptaPlanner 7:

    <solver>
        ...
        <moveThreadCount>availableProcessorCount - 1</moveThreadCount>
        ...
    </solver>

An example from the`solverConfig.xml`file in OptaPlanner 8:

    <solver>
        ...
        <moveThreadCount>1</moveThreadCount> <!-- Alternatively, use "AUTO" or omit entirely. -->
        ...
    </solver>

An example from the benchmarkConfig.xml file in OptaPlanner 7:

    <plannerBenchmark>
      ...
      <parallelBenchmarkCount>availableProcessorCount - 1</parallelBenchmarkCount>
      ...
    </plannerBenchmark>

An example from the benchmarkConfig.xml file in OptaPlanner 8:

    <plannerBenchmark>
      ...
      <parallelBenchmarkCount>1</parallelBenchmarkCount> <!-- Alternatively, use "AUTO" or omit entirely. -->
      ...
    </plannerBenchmark>

Removed the deprecated variable listeners

Major, Public API

The deprecated interface VariableListener from package org.optaplanner.core.impl.domain.variable.listener has ben removed, along with the deprecated interface StatefulVariableListener and the deprecated class VariableListenerAdapter in that same package. Use an interface VariableListener from package org.optaplanner.core.api.domain.variable instead.

An example of a VariableListener.java file in OptaPlanner 7:

  ...
  import org.optaplanner.core.impl.domain.variable.listener.VariableListenerAdapter;
  ...

  public class MyVariableListener extends VariableListenerAdapter<Object> {

    ...

    @Override
    void afterEntityRemoved(ScoreDirector scoreDirector, Object entity);
      ...
    }

    ...
  }

An example from a VariableListener.java file in OptaPlanner 8:

  ...
  import org.optaplanner.core.api.domain.variable.VariableListener;
  ...

  public class MyVariableListener extends VariableListener<MySolution, Object> {

    ...

    @Override
    void afterEntityRemoved(ScoreDirector<MySolution> scoreDirector, Object entity);
      ...
    }

    ...
  }

An example of a StatefulVariableListener.java file in OptaPlanner 7:

  ...
  import org.optaplanner.core.impl.domain.variable.listener.StatefulVariableListener;
  ...

  public class MyStatefulVariableListener implements StatefulVariableListener<Object> {

    ...

    @Override
    public void clearWorkingSolution(ScoreDirector scoreDirector) {
      ...
    }

    ...
  }

An example from the StatefulVariableListener.java file in OptaPlanner 8:

  ...
  import org.optaplanner.core.api.domain.variable.VariableListener;
  ...

  public class MyStatefulVariableListener implements VariableListener<MySolution, Object> {

    ...

    @Override
    public void close() {
      ...
    }

    ...
  }