JBoss.orgCommunity Documentation
Planner's input and output data (the planning problem and the best solution) are plain old JavaBeans (POJO's), so integration with other Java technologies is straightforward. For example:
To read a planning problem from the database (and store the best solution in it), annotate the domain POJO's with JPA annotations.
To read a planning problem from an XML file (and store the best solution in it), annotate the domain POJO's with XStream or JAXB annotations.
To expose the Solver as a REST Service that reads the planning problem and responds with the best
solution, annotate the domain POJO's with XStream or JAXB annotations and hook the Solver
in
Camel or RESTEasy.
Enrich the domain POJO's (solution, entities and problem facts) with JPA annotations to store them in a database.
Do not confuse JPA's @Entity
annotation with Planner's
@PlanningEntity
annotation. They can appear both on the same class.
Enrich the domain POJO's (solution, entities and problem facts) with XStream annotations to serialize them to/from XML.
Camel is an enterprise integration framework which includes support for Planner (starting from Camel 2.13). It can expose a use case as a REST service, a SOAP service, a JMS service, ...
Read the documentation for the camel-optaplanner component. That component works in Karaf too.
To deploy an Planner web application on WildFly, simply include the optaplanner dependency jars in the
war
file's WEB-INF/lib
directory (just like any other dependency) as shown
in the optaplanner-webexamples-*.war
. However, in this approach the war file can easily grow to
several MB in size, which is fine for a one-time deployment, but too heavyweight for frequent redeployments
(especially over a slow network connection).
The remedy is to use deliver the optaplanner jars in a JBoss module to WildFly and create a skinny war. Let's create an module called org.optaplanner:
Navigate to the directory ${WILDFLY_HOME}/modules/system/layers/base/
.
This directory contains the JBoss modules of WildFly. Create directory structure
org/optaplanner/main
for our new module.
Copy optaplanner-core-${version}.jar
and all its direct and transitive dependency
jars into that new directory. Use "mvn dependency:tree" on each optaplanner artifact to discover all
dependencies.
Create the file module.xml
in that new directory. Give it this content:
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.3" name="org.optaplanner">
<resources>
...
<resource-root path="kie-api-${version}.jar"/>
...
<resource-root path="optaplanner-core-${version}.jar"/>
...
<resource-root path="."/>
</resources>
<dependencies>
<module name="javaee.api"/>
</dependencies>
</module>
Navigate to the deployed war
file.
Remove optaplanner-core-${version}.jar
and all its direct and transitive
dependency jars from the WEB-INF/lib
directory in the war
file.
Create the file jboss-deployment-structure.xml
in the
WEB-INF/lib
directory. Give it this content:
<?xml version="1.0" encoding="UTF-8" ?>
<jboss-deployment-structure>
<deployment>
<dependencies>
<module name="org.optaplanner" export="true"/>
</dependencies>
</deployment>
</jboss-deployment-structure>
Because of WildFly's class loading, the method SolverFactory.createFromXmlResource()
might not find the solver configuration and throw an IllegalArgumentException
. Instead
use:
SolverFactory solverFactory = SolverFactory.createFromXmlInputStream(
getClass().getClassLoader().getResourceAsStream(
"org/optaplanner/examples/nqueens/solver/nqueensSolverConfig.xml"));
The optaplanner-core
jar includes OSGi metadata in its MANIFEST.MF
file to function properly in an OSGi environment too.
Furthermore, the maven artifact drools-karaf-features
(which will be renamed to
kie-karaf-features
) contains a features.xml
file that supports the
OSGi-feature optaplanner-engine
.
Planner does not require OSGi. It works perfectly fine in a normal Java environment too.
Android is not a complete JVM (because some JDK libraries are missing), but Planner works on Android with easy Java or incremental Java score calculation. The Drools rule engine does not work on Android yet, so Drools score calculation doesn't work on Android and its dependencies need to be excluded.
Workaround to use Planner on Android:
Add a dependency to the build.gradle
file in your Android project to exclude
org.drools
and xmlpull
dependencies:
dependencies {
...
compile('org.optaplanner:optaplanner-core:...') {
exclude group: 'xmlpull'
exclude group: 'org.drools'
}
...
}
A good Planner implementation beats any good human planner for non-trivial datasets. Many human planners fail to accept this, often because they feel threatened by an automated system.
But despite that, both can benefit if the human planner acts as supervisor to Planner:
The human planner defines and validates the score function.
Some examples expose a *Parametrization
object, which defines the weight for each
score constraint. The human planner can then tweak those weights at runtime.
When the business changes, the score function often needs to change too. The human planner can notify the developers to add, change or remove score constraints.
The human planner is always in control of Planner.
As shown in the course scheduling example, the human planner can lock 1 or more planning variables to a specific planning value and make those immovable. Because they are immovable, Planner does not change them: it optimizes the planning around the enforcements made by the human. If the human planner locks all planning variables, he/she sidelines Planner completely.
In a prototype implementation, the human planner might use this occasionally. But as the implementation matures, it must become obsolete. But do keep the feature alive: as a reassurance for the humans. Or in case that one day the business changes dramatically before the score constraints can be adjusted.
Therefore, it's often a good idea to involve the human planner in your project.