Chapter 19. Migration Guide
19.1. About the Business Resource Planner Migration
From Red Hat JBoss BRMS and Red Hat JBoss BPM Suite 6.1 onwards, Business Resource Planner is fully supported. Business Resource Planner is a lightweight, embeddable constraint satisfaction engine that is able to solve planning problems. Business Resource Planner includes a public API, which will be backwards compatible in later versions such as 6.2 and 6. For more information, see the Business Resource Planner documentation.
The following changes should be noted for implementation in JBoss BPM Suite/JBoss BRMS 6.1 onwards:
- Simulated Annealing now uses the time gradient of the current step instead of the time gradient of the last step. The impact of this change is negligible.
-
On AbstractScore, the methods
parseLevelStrings(…)
andbuildScorePattern(…)
have been changed from public to protected. It is highly unlikely that this affects your code. -
The
Descriptor
classes have been moved into a descriptor package.SolutionDescriptor.isInitialized(Solution)
now requires aScoreDirector
parameter. - There is now a better alternative to Brute Force: Branch And Bound, see docs for more information.
-
InverseRelationShadowVariableListener
has been renamed toSingletonInverseVariableListener
. It, andInverseRelationShadowVariableDescriptor
have moved to the package...impl.domain.variable.inverserelation
. -
The
ConstraintOccurrence
classes (which were deprecated) have been remove, and should be switched to theConstraintMatch
system. The interface
Solution
has been promoted to the public API. It has also moved package fromimpl.solution
toapi.domain.solution
.Previously in
*.java
:import org.optaplanner.core.impl.solution.Solution;
Now in
*.java
:import org.optaplanner.core.api.domain.solution.Solution;
All classes in the package
impl.move
have been moved toimpl.heuristic.move
. None of them are future-proof enough at this time to be added the public API. Prefer generic moves whenever possible.Previously in
*.java
:import org.optaplanner.core.impl.move.Move; import org.optaplanner.core.impl.move.CompositeMove; import org.optaplanner.core.impl.move.NoChangeMove;
Now in
*.java
:import org.optaplanner.core.impl.heuristic.move.Move; import org.optaplanner.core.impl.heuristic.move.CompositeMove; import org.optaplanner.core.impl.heuristic.move.NoChangeMove;
All classpath resources must lose their leading slash, because Business Resource Planner now expects them to adhere to
ClassLoader.getResource(String)
instead ofClass.getResource(String)
.The
SolverFactory.createFromXmlResource(String)
parameter must lose its leading slash.Previously in
*.java
:... = SolverFactory.createFromXmlResource( "/org/optaplanner/examples/cloudbalancing/solver/cloudBalancingSolverConfig.xml");
Now in
*.java
:... = SolverFactory.createFromXmlResource( "org/optaplanner/examples/cloudbalancing/solver/cloudBalancingSolverConfig.xml");
All elements
<scoreDrl>
must lose their leading slash.Previously in
*SolverConfig.xml
and*BenchmarkConfig.xml
:<scoreDrl>/org/optaplanner/examples/cloudbalancing/solver/cloudBalancingScoreRules.drl</scoreDrl>
Now in
*SolverConfig.xml
and*BenchmarkConfig.xml
:<scoreDrl>org/optaplanner/examples/cloudbalancing/solver/cloudBalancingScoreRules.drl</scoreDrl>
The
PlannerBenchmarkFactory.createFromXmlResource(String)
parameter must lose its leading slash.Previously in
*.java
:... = PlannerBenchmarkFactory.createFromXmlResource( "/org/optaplanner/examples/cloudbalancing/benchmark/cloudBalancingBenchmarkConfig.xml");
Now in
*.java
:... = PlannerBenchmarkFactory.createFromXmlResource( "org/optaplanner/examples/cloudbalancing/benchmark/cloudBalancingBenchmarkConfig.xml");
The
PlannerBenchmarkFactory.createFromFreemarkerXmlResource(String)
parameter must lose its leading slash.Previously in
*.java
:... = PlannerBenchmarkFactory.createFromFreemarkerXmlResource( "/org/optaplanner/examples/cloudbalancing/benchmark/cloudBalancingBenchmarkConfigTemplate.xml.ftl");
Now in
*.java
:... = PlannerBenchmarkFactory.createFromFreemarkerXmlResource( "org/optaplanner/examples/cloudbalancing/benchmark/cloudBalancingBenchmarkConfigTemplate.xml.ftl");
The
@PlanningVariable
property chained has been refactored tographType
. This is to allow support for other graph types (such as TREE) in the future.Previously in
*.java
:@PlanningVariable(chained = true, ...) public Standstill getPreviousStandstill() { return previousStandstill; }
Now in
*.java
:@PlanningVariable(graphType = PlanningVariableGraphType.CHAINED, ...) public Standstill getPreviousStandstill() { return previousStandstill; }
The
constructionHeuristicType
BEST_FIT
has been renamed intoWEAKEST_FIT
. The terminology “Best Fit” was not correct and did not allow forSTRONGEST_FIT
.Previously in
*SolverConfig.xml
and*BenchmarkConfig.xml
:<constructionHeuristic> <constructionHeuristicType>BEST_FIT</constructionHeuristicType> </constructionHeuristic>
Now in
*SolverConfig.xml
and*BenchmarkConfig.xml
:<constructionHeuristic> <constructionHeuristicType>WEAKEST_FIT</constructionHeuristicType> </constructionHeuristic>
The
constructionHeuristicType
BEST_FIT_DECREASING
has been renamed intoWEAKEST_FIT_DECREASING
. The terminology “Best Fit” was not correct and did not allow forSTRONGEST_FIT_DECREASING
.Previously in
*SolverConfig.xml
and*BenchmarkConfig.xml
:<constructionHeuristic> <constructionHeuristicType>BEST_FIT_DECREASING</constructionHeuristicType> </constructionHeuristic>
Now in
*SolverConfig.xml
and*BenchmarkConfig.xml
:<constructionHeuristic> <constructionHeuristicType>WEAKEST_FIT_DECREASING</constructionHeuristicType> </constructionHeuristic>
For the shadow variable of a bi-directional relationship, the declaration has changed from
@PlanningVariable(mappedBy)
to@InverseRelationShadowVariable(sourceVariableName)
.Previously in
*.java
:@PlanningVariable(mappedBy = "previousStandstill") Customer getNextCustomer(); void setNextCustomer(Customer nextCustomer);
Now in
*.java
:@InverseRelationShadowVariable(sourceVariableName = "previousStandstill") Customer getNextCustomer(); void setNextCustomer(Customer nextCustomer);
Multiple
<planningEntityClass>
elements now need to be ordered by superclasses (and superinterfaces) first, instead of superclasses (and superinterfaces) last.Previously in
*SolverConfig.xml
and*BenchmarkConfig.xml
:<planningEntityClass>...TimeWindowedCustomer</planningEntityClass> <planningEntityClass>...Customer</planningEntityClass> <planningEntityClass>...Standstill</planningEntityClass>
Now in
*SolverConfig.xml
and*BenchmarkConfig.xml
:<planningEntityClass>...Standstill</planningEntityClass> <planningEntityClass>...Customer</planningEntityClass> <planningEntityClass>...TimeWindowedCustomer</planningEntityClass>
The element
<planningEntityClass>
has been renamed to<entityClass>
.Previously in
*SolverConfig.xml
and*BenchmarkConfig.xml
:<planningEntityClass>org.optaplanner.examples.cloudbalancing.domain.CloudProcess</planningEntityClass>
Now in
*SolverConfig.xml
and*BenchmarkConfig.xml
:<entityClass>org.optaplanner.examples.cloudbalancing.domain.CloudProcess</entityClass>
XStreamScoreConverter
andXStreamBendableScoreConverter
have moved package.Previously in
*.java
:import org.optaplanner.persistence.xstream.XStreamScoreConverter;
Now in
*.java
:import org.optaplanner.persistence.xstream.impl.score.XStreamScoreConverter;
Previously in
*.java
:import org.optaplanner.persistence.xstream.XStreamBendableScoreConverter;
Now in
*.java
:import org.optaplanner.persistence.xstream.impl.score.XStreamBendableScoreConverter;
If you have a custom Move implementation, now extract
AbstractMove
.Previously in
*.java
:public class CloudComputerChangeMove implements Move {...}
Now in
*.java
:public class CloudComputerChangeMove extends AbstractMove {...}
19.2. Planning Values and Value Ranges
19.2.1. ValueRangeProvider
If you have a @ValueRangeProvider
that returns a collection of numbers (for example List<Integer>
or List<BigDecimal>
), then you should switch to a ValueRange
, which uses less memory and offers additional opportunities.
For example:
@ValueRangeProvider(id = "delayRange") public List<Integer> getDelayRange() { List<Integer> = new ArrayList<Integer>(5000); for (int i = 0; i < 5000; i++) { delayRange.add(i); } return delayRange; }
Is changed to:
@ValueRangeProvider(id = "delayRange") public CountableValueRange<Integer> getDelayRange() { return ValueRangeFactory.createIntValueRange(0, 5000); }
The annotation @ValueRangeProvider
has been moved into another package, from:
import org.optaplanner.core.api.domain.value.ValueRangeProvider;
to
import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
19.2.2. Planning Variables
The interface PlanningVariableListener
has been renamed to VariableListener
.
Previously in *.java
:
public class VehicleUpdatingVariableListener implements PlanningVariableListener<Customer> {
Now in *.java
:
public class VehicleUpdatingVariableListener implements VariableListener<Customer> {
The class AbstractPlanningVariableListener has been removed.
Previously in *.java
:
public class VehicleUpdatingVariableListener extends AbstractPlanningVariableListener<Customer> {
Now in *.java
:
public class VehicleUpdatingVariableListener implements VariableListener<Customer> {
The VariableListener is now declared on the shadow side, instead of the @PlanningVariable
side. This way, Business Rules Planner recognizes the shadow variables, and all shadow variables are declared in a consistent matter. Furthermore, it allows a shadow variable to based on other shadow variable.
Previously in *.java
:
@PlanningVariable(valueRangeProviderRefs = {"vehicleRange", "customerRange"}, graphType = PlanningVariableGraphType.CHAINED, variableListenerClasses = {VehicleUpdatingVariableListener.class, ArrivalTimeUpdatingVariableListener.class}) public Standstill getPreviousStandstill() { return previousStandstill; } public Vehicle getVehicle() { return vehicle; } public Integer getArrivalTime() { return arrivalTime; }
Now in *.java
:
@PlanningVariable(...) public Standstill getPreviousStandstill() { return previousStandstill; } @CustomShadowVariable(variableListenerClass = VehicleUpdatingVariableListener.class, sources = {@CustomShadowVariable.Source(variableName = "previousStandstill")}) public Vehicle getVehicle() { return vehicle; } @CustomShadowVariable(variableListenerClass = ArrivalTimeUpdatingVariableListener.class, sources = {@CustomShadowVariable.Source(variableName = "previousStandstill")}) public Integer getArrivalTime() { return arrivalTime; }
There is now out-of-the-box support for a shadow variable representing the anchor of a chained variable. For example, in a VRP each Customer (= entity) needs to know to which Vehicle (= anchor) it belongs. This declarative support allows build-in selectors to reuse that knowledge without duplicating the calculation.
Previously in *.java
:
@PlanningEntity public class Customer implements Standstill { @PlanningVariable(...) public Standstill getPreviousStandstill() {...} @CustomShadowVariable(variableListenerClass = VehicleUpdatingVariableListener.class, sources = {@CustomShadowVariable.Source(variableName = "previousStandstill")}) public Vehicle getVehicle() {...} } public class VehicleUpdatingVariableListener implements VariableListener<Customer> { ... }
Now in *.java
:
@PlanningEntity public class Customer implements Standstill { @PlanningVariable(...) public Standstill getPreviousStandstill() {...} @AnchorShadowVariable(sourceVariableName = "previousStandstill") public Vehicle getVehicle() {...} }
To scale VRP cases, Nearby Selection is critical. It is now finally completely supported and documented.
19.3. Benchmark
The internals of Benchmark have been deeply refactored to support the new aggregator functionality.
- The phrase "time spend" has been renamed to "time spent". This includes the log output and the benchmark report.
The <warmUp*>
elements have been renamed:
- The element <warmUpTimeMillisSpend> has been renamed to <warmUpMillisecondsSpentLimit>
- The element <warmUpSecondsSpend> has been renamed to <warmUpSecondsSpentLimit>
- The element <warmUpMinutesSpend> has been renamed to <warmUpMinutesSpentLimit>
- The element <warmUpHoursSpend> has been renamed to <warmUpHoursSpentLimit>
Previously in *BenchmarkConfig.xml
<plannerBenchmark> <warmUpTimeMillisSpend>...</warmUpTimeMillisSpend> <warmUpSecondsSpend>...</warmUpSecondsSpend> <warmUpMinutesSpend>...</warmUpMinutesSpend> <warmUpHoursSpend>...</warmUpHoursSpend> ... </plannerBenchmark>
Now in *BenchmarkConfig.xml
:
<plannerBenchmark> <warmUpMillisecondsSpentLimit>...</warmUpMillisecondsSpentLimit> <warmUpSecondsSpentLimit>...</warmUpSecondsSpentLimit> <warmUpMinutesSpentLimit>...</warmUpMinutesSpentLimit> <warmUpHoursSpentLimit>...</warmUpHoursSpentLimit> ... </plannerBenchmark>
The class XmlPlannerBenchmarkFactory
has been removed and replaced by static methods on PlannerBenchmarkFactory
.
Previously in *.java
:
PlannerBenchmarkFactory plannerBenchmarkFactory = new XmlPlannerBenchmarkFactory(...);
Now in *.java
:
PlannerBenchmarkFactory plannerBenchmarkFactory = PlannerBenchmarkFactory.createFromXmlResource(...);
If you used the method addXstreamAnnotations()
, take a look at the non-public API class XStreamXmlPlannerBenchmarkFactory
.
The element <xstreamAnnotatedClass>
has been renamed to <xStreamAnnotatedClass>
.
Previously in *BenchmarkConfig.xml
<problemBenchmarks> <xstreamAnnotatedClass>org.optaplanner.examples.nqueens.domain.NQueens</xstreamAnnotatedClass> ... </problemBenchmarks>
Now in *BenchmarkConfig.xml
:
<problemBenchmarks> <xStreamAnnotatedClass>org.optaplanner.examples.nqueens.domain.NQueens</xStreamAnnotatedClass> ... </problemBenchmarks>
19.3.1. SolutionFileIO
ProblemIO
has been renamed to SolutionFileIO
and moved package (into the public API).
Previously in *.java
:
import org.optaplanner.core.impl.solution.ProblemIO; public class MachineReassignmentFileIO implements ProblemIO { ... }
Now in *.java
:
import org.optaplanner.persistence.common.api.domain.solution.SolutionFileIO; public class MachineReassignmentFileIO implements SolutionFileIO { ... }
Now in *.java
:
Previously in *SolverConfig.xml
and *BenchmarckConfig.xml
:
<problemBenchmarks> <problemIOClass>...MachineReassignmentProblemIO</problemIOClass> ... </problemBenchmarks>
Now in *SolverConfig.xml
and *BenchmarckConfig.xml
:
<problemBenchmarks> <solutionFileIOClass>...MachineReassignmentFileIO</solutionFileIOClass> ... </problemBenchmarks>
The method SolutionFileIO.getFileExtension()
has been split up in getInputFileExtension()
and getOutputFileExtension();
. It is still highly recommended to use the same input and output file extension.
Previously in *.java
:
public String getFileExtension() { return FILE_EXTENSION; }
Now in *.java
:
public String getInputFileExtension() { return FILE_EXTENSION; } public String getOutputFileExtension() { return FILE_EXTENSION; }
19.4. Solver Configuration
In Solver and BestSolutionChangedEvent, the method getTimeMillisSpend()
has been renamed to getTimeMillisSpent()
.
Previously in *.java
:
... = solver.getTimeMillisSpend();
Now in *.java
:
... = solver.getTimeMillisSpent();
Previously in *.java
:
public void bestSolutionChanged(BestSolutionChangedEvent event) { ... = event.getTimeMillisSpend(); }
Now in *.java
:
public void bestSolutionChanged(BestSolutionChangedEvent event) { ... = event.getTimeMillisSpent(); }
The solver phase <bruteForce>
has been replaced by <exhaustiveSearch>
's BRUTE_FORCE
type.
Previously in *SolverConfig.xml
and *BenchmarkConfig.xml
:
<bruteForce/>
Now in *SolverConfig.xml
and *BenchmarkConfig.xml
:
<exhaustiveSearch> <exhaustiveSearchType>BRUTE_FORCE</exhaustiveSearchType> </exhaustiveSearch>
The methods setPlanningProblem(Solution)
and solve()
have been merged as the method solve(Solution)
.
Previously in *.java
:
solver.setPlanningProblem(planningProblem); solver.solve();
Now in *.java
:
solver.solve(planningProblem);
You still need to use solver.getBestSolution()
to retrieve the best solution. That is intentional due to real-time planning and to support pare to optimization in the future.
The class XmlSolverFactory
(which was not part of the public API) has been removed and replaced by static methods on SolverFactory
(which are part of the public API).
Previously in *.java
:
SolverFactory solverFactory = new XmlSolverFactory("...solverConfig.xml");
Now in *.java
:
SolverFactory solverFactory = SolverFactory.createFromXmlResource("...solverConfig.xml");
Previously in *.java
:
SolverFactory solverFactory = new XmlSolverFactory().configure(inputStream);
Now in *.java
:
SolverFactory solverFactory = SolverFactory.createFromXmlInputStream(inputStream);
Previously in *.java
:
SolverFactory solverFactory = new XmlSolverFactory().configure(reader);
Now in *.java
:
SolverFactory solverFactory = SolverFactory.createFromXmlReader(reader);
If you used the method addXstreamAnnotations()
, take a look at the non-public API class XStreamXmlSolverFactory
.
The following changes have been made to the Custom SolverPhase:
-
The interface
CustomSolverPhaseCommand
has been renamed toCustomPhaseCommand
. -
The element
<customSolverPhase>
has been renamed to<customPhase>
. -
The element
<customSolverPhaseCommandClass>
has been renamed to>customPhaseCommandClass>
.
Previously in *.java
:
public class ToOriginalMachineSolutionInitializer implements CustomSolverPhaseCommand { ... }
Now in *.java
:
public class ToOriginalMachineSolutionInitializer implements CustomPhaseCommand { ... }
Previously in *SolverConfig.xml
and *BenchmarkConfig.xml
:
<customSolverPhase> <customSolverPhaseCommandClass>...ToOriginalMachineSolutionInitializer</customSolverPhaseCommandClass> </customSolverPhase>
Now in *SolverConfig.xml
and *BenchmarkConfig.xml
:
<customPhase> <customPhaseCommandClass>....ToOriginalMachineSolutionInitializer</customPhaseCommandClass> </customPhase>
The method ScoreDefinition.getLevelCount()
has been renamed to ScoreDefinition.getLevelsSize()
.
19.5. Optimization
19.5.1. Termination
All child elements of <termination>
have been renamed:
-
The element
<maximumTimeMillisSpend>
has been renamed to<millisecondsSpentLimit>
-
The element
<maximumSecondsSpend>
has been renamed to<secondsSpentLimit>
-
The element
<maximumMinutesSpend>
has been renamed to<minutesSpentLimit>
-
The element
<maximumHoursSpend>
has been renamed to<hoursSpentLimit>
-
The element
<scoreAttained>
has been renamed to<bestScoreLimit>
-
The element
<maximumStepCount>
has been renamed to<stepCountLimit>
-
The element
<maximumUnimprovedStepCount>
has been renamed to<unimprovedStepCountLimit>
Configuration in *SolverConfig.xml
and *BenchmarkConfig.xml
has changed from:
<termination> <maximumTimeMillisSpend>...</maximumTimeMillisSpend> <maximumSecondsSpend>...</maximumSecondsSpend> <maximumMinutesSpend>...</maximumMinutesSpend> <maximumHoursSpend>...</maximumHoursSpend> <scoreAttained>...</scoreAttained> <maximumStepCount>...</maximumStepCount> <maximumUnimprovedStepCount>...</maximumUnimprovedStepCount> </termination>
to:
<termination> <millisecondsSpentLimit>...</millisecondsSpentLimit> <secondsSpentLimit>...</secondsSpentLimit> <minutesSpentLimit>...</minutesSpentLimit> <hoursSpentLimit>...</hoursSpentLimit> <bestScoreLimit>...</bestScoreLimit> <stepCountLimit>...</stepCountLimit> <unimprovedStepCountLimit>...</unimprovedStepCountLimit> </termination>
19.5.2. Events
Classes BestSolutionChangedEvent
and SolverEventListener
moved from package impl.event to api.solver.event
. They are now part of the public api.
Previously in *.java
:
import org.optaplanner.core.impl.event.BestSolutionChangedEvent; import org.optaplanner.core.impl.event.SolverEventListener;
Now in *.java
:
import org.optaplanner.core.api.solver.event.BestSolutionChangedEvent; import org.optaplanner.core.api.solver.event.SolverEventListener;
19.5.3. Score Trends
Specify an <initializingScoreTrend>
in the <scoreDirectorFactory>
to increase performance of some algorithms (Construction Heuristics and Exhaustive Search).
See the documentation section on InitializingScoreTrend
when to use ANY
, ONLY_UP
, or ONLY_DOWN
.
Previously in *SolverConfig.xml
and *BenchmarkConfig.xml
:
<scoreDirectorFactory> <scoreDefinitionType>HARD_SOFT</scoreDefinitionType> <scoreDrl>.../cloudBalancingScoreRules.drl</scoreDrl> </scoreDirectorFactory>
Now in *SolverConfig.xml
and *BenchmarkConfig.xml
:
<scoreDirectorFactory> <scoreDefinitionType>HARD_SOFT</scoreDefinitionType> <scoreDrl>.../cloudBalancingScoreRules.drl</scoreDrl> <initializingScoreTrend>ONLY_DOWN</initializingScoreTrend> </scoreDirectorFactory>
Replace <pickEarlyType>
FIRST_NON_DETERIORATING_SCORE
with <initializingScoreTrend>
ONLY_DOWN
. If the <initializingScoreTrend>
is specified, the <constructionHeuristic>
will automatically use the most appropriate <pickEarlyType>
.
Previously in *SolverConfig.xml
and *BenchmarkConfig.xml
:
<scoreDirectorFactory> ... </scoreDirectorFactory> ... <constructionHeuristic> <constructionHeuristicType>FIRST_FIT_DECREASING</constructionHeuristicType> <forager> <pickEarlyType>FIRST_NON_DETERIORATING_SCORE</pickEarlyType> </forager> </constructionHeuristic>
Now in *SolverConfig.xml
and *BenchmarkConfig.xml
:
<scoreDirectorFactory> ... <initializingScoreTrend>ONLY_DOWN</initializingScoreTrend> </scoreDirectorFactory> ... <constructionHeuristic> <constructionHeuristicType>FIRST_FIT_DECREASING</constructionHeuristicType> </constructionHeuristic>
19.5.4. Score Calculator
The interface SimpleScoreCalculator
has been renamed to EasyScoreCalculator
to avoid confusion with SimpleScore
and SimpleScore:
it can return other Score types. The package name has also changed.
Previously in *.java
:
import org.optaplanner.core.impl.score.director.simple.SimpleScoreCalculator; public class CloudBalancingEasyScoreCalculator implements SimpleScoreCalculator<CloudBalance> { ... }
Now in *.java
:
import org.optaplanner.core.impl.score.director.easy.EasyScoreCalculator; public class CloudBalancingEasyScoreCalculator implements EasyScoreCalculator<CloudBalance> { ... }
Previously in *SolverConfig.xml
and *BenchmarkConfig.xml
:
<simpleScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.solver.score.CloudBalancingEasyScoreCalculator<simpleScoreCalculatorClass>
Now in *SolverConfig.xml
and *BenchmarkConfig.xml
:
<easyScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.solver.score.CloudBalancingEasyScoreCalculator
The BendableScore
configuration has changed: …LevelCount
has been renamed to …LevelsSize
.
Previously in *SolverConfig.xml
and *BenchmarkConfig.xml
:
<scoreDirectorFactory> <scoreDefinitionType>BENDABLE</scoreDefinitionType> <bendableHardLevelCount>2</bendableHardLevelCount> <bendableSoftLevelCount>3</bendableSoftLevelCount> ... </scoreDirectorFactory>
Now in *SolverConfig.xml
and *BenchmarkConfig.xml
:
<scoreDirectorFactory> <scoreDefinitionType>BENDABLE</scoreDefinitionType> <bendableHardLevelsSize>2</bendableHardLevelsSize> <bendableSoftLevelsSize>3</bendableSoftLevelsSize> ... </scoreDirectorFactory>