Last week I created a “Worker Planner” that has a nice GUI deployed on Apache Tomcat and it works in sync with a remote Scheduling Decision Service deployed as AWS Lambda. The scheduling service was implemented with JavaSolver and a constraint solver included into JSR331. My objective was to demonstrate that these days with cloud-based deployment it is not so difficult to create an end-to-end full-scale decision optimization service. I also wanted to show how to apply powerful Linear Solvers to crack traditionally complex production scheduling problems. So, two days ago I took a well-known problem that is described in this example:
This problem was used as a basic example of a linear optimization problem by JSR331 and JavaSolver. Of course, I again wanted to create a nice, easy-to-use GUI that defines as many products as a customer wants and that calls a remote decision optimization service to quickly receive optimal decision. In this case, such decision will recommend how many products should be produced internally and how many should be purchased to minimize the total cost. Here is how I did it.
I started with a GUI that should define our problem using the following table:
In this table a user may add (delete) as many products as he/she needs, and for each of them enter Inside/outside production costs, demand, and resource consumption. The resource availability can be defined in the following table:
After entering input data, a user should be able to click on the button “Optimize” to generate an optimal solution that may be presented similarly to the following table:
It is really simple to build this GUI using any form builder, but I again used OpenRules Dialog that allowed me to define these 3 tables in simple Excel tables, and deploy this web application on Apache Tomcat. It took me several hours to tune all everything to get this web interface up and running. Here is the resulting GUI:
There is nothing special here and initially all results were empty because my button “Optimize” did nothing. This application uses 3 basic Java classes: Product, Resource, and BusinessProblem that is a placeholder for all products and resources. BusinessProblem also includes methods “addProduct()” and “deleteProduct()“. My OpenRules Dialog project “InsideOutsideProduction”also contains the Java class UserInterface responsible for user interaction. In particular, it include the method “optimize()” associated with the button “Optimize” that initially did nothing. You can look in the Excel file Main.xls to see how the GUI was defined.
So, after successfully debugging the GUI using my local Tomcat server, I was able to concentrate Decision Optimization service.
Developing Decision Optimization Service
This time instead of AWS Lambda, I decided to deploy the future decision optimization service on another Tomcat server, that later can be uploaded to AWS EC2 instance or any other cloud platform. While this service still would use JavaSolver, instead of a constraint solver I wanted to use one of the Linear Solvers included in the current implementation of the JSR331 standard (Coin, Scip, GLPK, or lp_solve). All these open-source linear solvers provide their executable files that should be accessible from a remote machine on which my future decision service will run. It made the use of AWS Lambda impossible, and I decided to deploy it on another Tomcat just using different port (8081 instead of the port 8080 already used by the GUI).
The basic implementation of such service (limited to hard-coded 2 resources and 3 products) was already available from JavaSolver. So, I only needed to teach it how to accept any arrays of products and resources using the REST API. It can be easily done if I wrap the basic Java implementation into a standard OpenRules Decision Manager. So, I copied the standard project project “VacationDaysSpringBoot” into a new project “InsideOutsideProduction”, removed all rules, and added the following Excel-based test data:
I also added the following Glossary.xls:
My service does not need any rules, but it needs to use the same Java classes BusinessProblem, Product, and Resource that I already used in the GUI. So, copied these Java classes to my “InsideOutsideProduction” project. I adjusted the configuration file “project.properteis” as follows:
That was enough to build and test my OpenRules Decision Manager’s project. As usual, I simply double-clicked on the “run.bat”. and it produced positive results:
I wanted to integrate the GUI and the optimization service ASAP. My integration schema looked as in the following schema:
A GUI on the first Tomcat should ask an Optimization Service on the second Tomcat for an optimal solution for the current products and resources, and display the optimal results back on the GUI.
My first Tomcat was already running. I needed to start the second Tomcat. So, I double-clicked on the standard OpenRules file “runLocalServer.bat” to deploy my optimization service. However, it immediately produced the error telling me that another Tomcat (the one used for GUI) is already running. So, I needed to start the second Tomcat using another port, say 8081. To do this, I simply modified my “pom.xml” file by adding a configuration section to the SpringBoot plugin:
Then my “runLocalServer.bat” started successfully, and I was able to test it (also successfully!) using the standard OpenRules file “testLocalServer.bat”.
Now I needed to create a Java test that will execute this service and will be a prototype for an implementation of the button “Optimize” in my GUI. OpenRules Decision Manager provides the standard class “DecisionServiceClient” (publicly available from the GitHub repository “openrules.tools”) that is usually used to create custom clients for remote OpenRules services. So, I created the following Java class “SimpleClient” to test my server:
It produced the same results as above.
So, now I needed to go back to my GUI, and similarly implement the button “Optimize”. First, I created a new Java class OptimizationService in my OpenRules Dialog project “InsideOutsideProduction”. It looks very similar to the SimpleClient but instead use test-objects defined in Excel it uses Java objects passes from the GUI:
Pay attention, that I added the method “save” to save the produced results from the “responseProblem” to the initial “buinessProblem”.
Now I only needed to associate the the botton “Optimize” with this services. To do this, I added the following method “optimize()” of my internal class UserInterface (that so far was empty):
I only needed to double-click on “deploy.bat” to re-deploy my GUI ant to restart its Tomcat on the standard port 8080. So, both Tomcats now were running. It displayed a GUI similar to the When I received the above one. I click on the button “Optimize” I received some initial results in the table “Results” the looked reasonable on the first glance. Thus, I got my application working – my GUI on the first Tomcat called an optimization service on the second Tomcat that quickly returned the optimization solution displayed back on the GUI. So, this schema worked, and worked super-fast!
When I added more products, I noticed that the results were not actually optimal. Why? I tried different resource consumption values, and got a suspicion that something is wrong with the consumption constraints. So, I went back to my optimization service. After more serious analysis, I understood that the old JavaSolver’s implementation of this example had an error: the consumption constraints were not properly implemented. So, I made the needed changes (you can see the source code in this Java class) and tested its again locally. When I received the correct optimization solutions, I redeployed my optimization service again on the Tomcat-2. Without restarting Tomcat-1 (!), I clicked again on the button “Optimize” and received the correct results as on the above GUI. Thus, when my integration already worked, I was able to improve my optimization server, or even switch to a different implementation, e.g. using another underlying linear solver.
I decided to try different linear solvers. So, I replaced the last dependency in my “pom.xml” from “jsr331-scip” to “jsr331-glpk” to try the GLPK solver:
Then I just re-built and re-started my second Tomcat. The integrated GUI continued to work fine with the GLPK solver. It’s interesting that when I tried two other linear solvers Coin and lp_solve, they both failed – I’ve seen many times that some solvers not always work fine on problems for which other solvers succeed. So, it’s nice that JSR331 allows me so easily to try different solvers without changing a character in my decision optimization model.
Now, it’s only a matter of configuration to deploy both my GUI and Optimization service on the remote Tomcats or Docker containers.
The main objective of the quick development of two optimization services (WorkerPlanner and InsideOutsideProduction) was to demonstrate that these days it is not so difficult to create an end-to-end Decision Optimization services. It really helps to have an access to a healthy choice of open-source and commercial optimization solvers. The way how we separated GUI and Optimization Services allows us to continue to improve these services without any changes in the graphical interfaces or other clients. The simplicity of deploying such services on cloud makes these applications secure, highly scalable, and available from almost “everywhere”.