Retrieve Best Solution

Building Optaplanner Applications using the BRMS Business Central GUI: The Missing Guide

1 Introduction

A potential customer is interested in seeing how BRMS Business Central can help him build an Optaplanner (also known as Business Resource Planner) application using the GUI alone after I demonstrated to him the Optometrist rostering app based on the Nurse Rostering sample application.

Optometrist Rostering App
Optometrist Rostering App

I wanted to create a simple Optaplanner app so I picked cloud balancing. The problem statement for cloud balancing can be found here:

A sample implementation can be downloaded here:

However, the rules in the sample implementation are in native .DRL syntax. The customer wants to see everything created using Business Central GUI tools, if possible. This is the reason why I wanted to re-create this example using Business Central.

Please note that you have to add the “plannermgmt” and “kie-server” roles to you user login to Business Central in order to see the Optaplaner-specific GUI and manage the execution server.

2 When Experience Fails

There is not much documentation on how to use the Optaplanner GUI tools on Business Central (BC). There are bits and pieces here and there but not a complete description on how to build an Optaplanner app from scratch. Without proper documentation, you may encounter issues when building your rules using the Guided Rule Editor. The more so if you are familiar with BC tools. This may sound counter intuitive but is is true. Let me tell you why.

Here is how one of the .drl rules looks like:

rule "requiredMemoryTotal"
when
  $computer : CloudComputer($memory : memory)
  $requiredMemoryTotal : Number(intValue > $memory) from    accumulate(
    CloudProcess(
      computer == $computer,
      $requiredMemory : requiredMemory),
      sum($requiredMemory)
    )
then
  scoreHolder.addHardConstraintMatch(kcontext, $memory -   $requiredMemoryTotal.intValue());
end

The thing to note is scoreHolder which is an Optaplanner org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScoreHolder object. For anyone who has worked with BC Guided Rule Editor before, you know that the first thing to do is to add the data object from the “Data Objects” tab.
addDataObject

However, the HardSoftScoreHolder class is nowhere to be found when you click on the “+New Item” button. I tried hard to make that class appear in the drop down list. Things I did include:
1) Using Authoring→Artifact repository to upload optaplanner-core-6.5.0-Final-readhat-2.jar as that is the jar that contains the class I need. It displayed an error message but the jar was uploaded and a pom created.
2) Adding the entry: “Optaplanner org.optaplanner.core.api.score.buildin.hardsoft.*” in the page-names-white-list file so that the class imported appear in the dropdown list.

After doing these, the class did appear and I can create the rules but came build time, I ran into lots of problems:

Caused by: org.eclipse.aether.resolution.ArtifactResolutionException: Failure to transfer org.javassist:javassist:pom:3.19.0.GA-redhat-1 from http://localhost:8080/business-central/maven2/ was cached in the local repository, resolution will not be reattempted until the update interval of guvnor-m2-repo has elapsed or updates are forced. Original error: Could not transfer artifact org.javassist:javassist:pom:3.19.0.GA-redhat-1 from/to guvnor-m2-repo (http://localhost:8080/business-central/maven2/): Unauthorized (401)
...
ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/business-central].[M2Servlet]] (http-localhost/127.0.0.1:8080-3) JBWEB000236: Servlet.service() for servlet M2Servlet threw exception: java.lang.RuntimeException: org.eclipse.aether.deployment.DeploymentException: Failed to deploy artifacts: Could not transfer artifact org.optaplanner:optaplanner-core:jar:6.5.0.Final-redhat-2 from/to jboss-releases-repository (https://origin-repository.jboss.org/nexus/service/local/staging/deploy/maven2/): Unauthorized (401)

It turns out that you do not have to do any of the things I did. How I wished someone had told me before I went down this path. Then I cloned the sample implementation onto BC and see how it works. And I am going to document what is needed to create such an application from scratch so that you don’t have to waste time like I did.

3 The Missing Guide

3.1 Building the App

I am not going to tell you how to create a project on BC. I assume you all know how to do it. You need to do the following (some of the steps need not be in strict order) after creating the project:

3.1.1 Create Data Models

I created 3 classes (the data models for cloud balancing) using the Data Modeler: Computer, Process and Solution.

Computer Data Model
Computer Data Model
Process Data Model
Process Data Model
Solution Data Model
Solution Data Model

3.1.2 Annotate Your Data Models

We have to tell Optaplanner regarding the planning entity, planning variable, valueRangeProviderRef, planning solution, etc. using annotations. All these are achieved using the BC GUI tool as can be seen in the following diagrams.

Process Planning Entity
Process Planning Entity
Process Planning Variable Range
Process Planning Variable Range
Planning Solution
Planning Solution
Solution ValueRange
Solution ValueRange

3.1.3 Define Rules using Guided Rules Editor without DSL

Then I defined the costRule and the cpuRule by using the Guided Rule Editor without using DSL to get a feel of the complexity involved. Note that in both cases, we have to use the ugly “Add free form DRL” action to define the THEN part of the rule. The BU says enhancement is being developed to alleviate the need to drop down to Java code in the Guided Rule Editor. The cpuRule, although not complex in .drl terms, is tedious to construct using the Guided Rule editor due to the use of “from accumulate” Conditional Element. The rules (in Editor as well as in source Views) are shown below:

costRule Editor View
costRule Editor View
costRule Source View
costRule Source View
cpuRule Editor View
cpuRule Editor View
cpuRule Source View
cpuRule Source View

3.3.4 Define a DSL

One way to make the rules easier to understand and construct is to create a DSL as shown:

DSL Definition
DSL Definition

3.1.5 Define Rules Using Guided Rules Editor with DSL

Now that we have defined a DSL, the construction of the memoryRule is a lot simpler.

memoryRule Editor View
memoryRule Editor View
memoryRule Source View
memoryRule Source View

3.1.6 Define Solver Configuration

Use Authoring→Solver Config to create a solver configuration. Pick Hard_Soft and enter the termination criterion.

Solver Configuration
Solver Configuration

3.1.7 Define External Data Object

If you try to build the app now, it will fail because it still require 2 more things to be done. Click on the “Open Project Editor” and then select “External Data Objects” from the dropdown listbox on the Project editor pane. Add an external data object (Optaplanner org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScoreHolder ) as shown:

External Data Objects
External Data Objects

3.1.8 Define Global Definition

Doing 3.1.7 alone is not sufficient, we also need to define a global variable (scoreHolder) to be used in the THEN part of the rules. To do this click on “New Item” and select Global Definition.

Global Definitions
Global Definitions

The alternative to doing Steps 3.1.7 and 3.1.8, as suggested by Matej Cimbora (mcimbora@redhat.com) in the sme-brms forum, is to create a .drl file in your project with content:

package opta.curriculumcourse;

import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScoreHolder;

global HardSoftScoreHolder scoreHolder;

This works but as our goal is not to use native .drl files, I am sticking with my approach ie, doing steps 3.1.7 and 3.1.8.

3.1.9 What about Testing the Rules?

I defined a test scenario called memoryTest. When I execute the test, it always results in error.

memoryTest Error
memoryTest Error

I’ve tried modifying the kie-deployment-descriptor.xml by adding a global element inside of the globals element hoping that this will instantiate the global variable making the test work. It did not work.

—–

Matej Cimbora (mcimbora@redhat.com) explained why such tests won’t work:

...From what I see, the rule got indeed fired (see the exception - Exception executing consequence for rule "memoryRule" in ...). The problem is that the scoreHolder variable gets initialized internally by the OptaPlanner engine. Scenario runner has no awareness of the OptaPlanner-specific functionality, therefore it leaves the scoreHolder reference null, which causes the exception. I created [1] to resolve the issue…
[1] https://issues.jboss.org/browse/PLANNER-720

Thanks Matej for logging the issue. You are indeed a Mate like your namesake ;-)
So, don’t waste your time trying to get your test scenarios to work. Test Scenarios are not working for Optaplanner apps yet.

3.1.10 Build and Deploy

There is nothing special in creating a kie-container to run our app. My kie-container’s name is ‘myplanner’.

MyPlanner KIE-container
MyPlanner KIE-container

4 Testing using SoapUI

Since I already have SopaUI on my machine, I am going to use it for testing. You create a REST project and give it the url:

http://localhost:8080/kie-server/services/rest/server/containers/myplanner/solvers/myplanner

You will be using the Optaplanner REST API to interact with the Optaplanner app you deployed earlier.

Please note that soapui (at lease the version I use V5.2.1) has the nasty habit of changing the URL (resource) for all you request once you changed one. In this case, different request should have different URLs. It also changed the operation (eg, PUT) for all requests once you changed one.

You need to set the header for the following requests with:

X-KIE-ContentType: xstream
Content-Type: application/xml

4.1 Create a New Solver

You need to issue a PUT to http://localhost:8080/kie-server/services/rest/server/containers/myplanner/solvers/myplanner
with:

<solver-instance>
	<solver-config-file>demo/myplanner/CloudBalance.solver.xml</solver-config-file>
</solver-instance>

The first myplanner is the name of the kie-container I created on the server
the second myplanner is the name of the solver instance I want to create

Create New Solver Instance
Create New Solver Instance

4.2 Start Solving

Issue a POST to http://localhost:8080/kie-server/services/rest/server/containers/myplanner/solvers/myplanner
with:

<solver-instance>
	<status>SOLVING</status>
	<planning-problem class="demo.myplanner.Solution">
		<computerList>
			<demo.myplanner.Computer>
				<memory>4</memory><cost>2000</cost>
			</demo.myplanner.Computer>
			<demo.myplanner.Computer>
				<memory>8</memory><cost>3000</cost>
			</demo.myplanner.Computer>
			<demo.myplanner.Computer>
				<memory>16</memory><cost>4000</cost>
			</demo.myplanner.Computer>
		</computerList>
		<processList>
			<demo.myplanner.Process>
				<requiredMemory>1</requiredMemory>
			</demo.myplanner.Process>
			<demo.myplanner.Process>
				<requiredMemory>7</requiredMemory>
			</demo.myplanner.Process>
		</processList>
	</planning-problem>
</solver-instance>
Start Solving
Start Solving

4.3 Get Best Result

Issue a GET to http://localhost:8080/kie-server/services/rest/server/containers/myplanner/solvers/myplanner/bestsolution to retrieve the best solution.

Retrieve Best Solution
Retrieve Best Solution

You will notice that the solution says computer 2 should be used to run both processes. Note that xpath array index start at 1 and not 0, computer 2 is the second computer.

5 Conclusion

In my humble opinion, the official BRMS documentation on using the Business Central GUI to build an Optaplanner application requires improvement. By documenting my experience in using the Business Central GUI tools to create an Optaplanner project, deploy and interact with it, I am hoping that you don’t have to go through all the hurdles that I had to go through. You are welcome to use it as the Missing Guide to help you create Optaplanner applications using the Business Central GUI.