ats-rule-engine

Phases

The current project is curently envisioned as 2 broad phases.

  • drawing validation
  • drawing compostion

Each of the phases will have a final deliverable product consisting of one of more web services with correstpoding command line tools.

Technology Choices

For each of these phases, webservices will be implemented and delivered as spring-boot applications implemented in the groovy programing language. Command line application will be imlemented in groovy.

Groovy allows for rapid development providing a terse, highly expressive, customizable language which is tightly bound with the JVM and has full access to all Java's extensive collection of libraries

As the delivered products are all implemented as services, it is expected that they will be deployed to unxi-like server class machine based on either linux or OS/X. Deployment guarantees are currently constrained to those environments. While it is expected that the delivered product will be able to run under Windows, if so required, that specific configuration is not one which is regularily tested in the course of development.

The language for rule specification will be a custom Domain Specific Language (DSL) based on groovy, selected for it's power of expession and user-friendliness

Security constraints

It is assumed that security considerations are to be repected insofar as delivered webservices will be expected to integrate with ATS's existing infrastructure.

spring-boot

Spring Boot allows us to implement web services as robust stand-alone binaries.

install sdkman and all requisite tools

This section is of interest to developers only and may be ignored for deployment considerations.

visit SDKMON for details about this impressive tool.

Recommended for common Unix: linux, OS/X, cygwin, FreeBSD, Solaris

Install the command sdk

quick method

curl -s "https://get.sdkman.io" | bash 

careful method

curl -s "https://get.sdkman.io" > donottrust 
## check for NSA footprint
less donottrust
## if satisfied
cat donottrust | bash

You may need to reload or restart your shell to make the sdk command available

These commands all play nicely with the system's update-alternatives module


sdk install java
sdk install groovy
sdk install maven
sdk install gradle
sdk install springboot

Building

The only prerequisites to a successful build are

  • availability of a Java 8 SDK
  • network access to jcentral, a maven repository from which all dependencies will be loaded.

Explicit dependencies are listed in build.gradle . The full list of required dependencies can be obtained with the command ./gradlew dependencies

To build the application:

./gradlew build

This will produce an executeable jar file, build/libs/ats-rule-engine.jar containing the application, embedded Tomcat and all dependencies. The jar is fully relocatable and may be run on any system with a Java 8 SDK.

The schema provided in schema/schema.sql must be loaded onto a MySQL 5.6+ database and a user created with READ/WRITE access to that database.

Database connection information must be entered into a properties file, the path of which is passed to the application at runtime.

contents of mydb.properties

username=ruleengine
password=mypassword
database=ats_rules
host=localhost

The name of the database configuration file is passed as a system property: ie. -Ddb.properties=mydb.properties

If the property db.properties is not defined, the rule engine will atempt to obtain connection information directly from system properties with the following property names:

  • db.username
  • db.password
  • db.database
  • db.host

ie.

java -Ddb.host=localhost -Ddb.username=ruleengine -Ddb.password=mypassword -Ddb.database=ats_rules -jar ats-rule-engine.jar 

The ruleset(s) to be used by the application may also be specified as a system property: ie. -Druleset=ats2,ats3,experiment

If no ruleset is specified for the application, the ruleset named core will be loaded

It should be noted that, until that schema is populated with relevant information, the application does nothing vrey interesting at all.

Running

The runtime host must be provided with a Java 8 SDK. It was developed and tested under OpenJDK but any vendor should be sufficient.

Note

A JRE is not an SDK. The Java SDK/JDK is a runtime requirement for Tomcat (or any other servlet container) and groovy, in which language most of this application is written. Please check that you have a Java SDK installed.

Configuring

This will launch the application with the combined rulesets ats2 and ats3, using mydb.properties to specify database connection information

java -Ddb.properties=mydb.properties -Druleset=ats2,ats3 -jar ats-rule-engine.jar 

Like most typical spring-boot webservice applications, the application is intended to be shut down via the bullet-to-the-head method.

Any method preferred by the operator may be employed as the service is essentially a read-only service and will not leave broken data as a result of an abort.

For example, the application can be shutdown with a hup signal or merely by shuting down the host container or host on which it is running.

It is expected that in production operation, multiple instances of the webservice could be launched with a proxy (ie. nginx) acting as a pool manager/front-end.

A given instance would be launched before being added to the service pool and removed from the pool before it's instance is shutdown. This should ensure uninterrupted operation.

Endpoints

All output from this service are type application/json.
All POST/PUT contents are also type application/json

  • POST /validate ## a json list of products per items3.json
  • GET /categories ## show all available categories
  • GET /reload ## refresh the rules from the database
  • GET /rule ## show all rules loaded in the service
  • PUT /rule/{uuid} ## modify an existing rule identified by UUID (not implemented)
  • POST /rule ## create a rule
  • DELETE /rule ## delete a rule (not implemented)
  • GET /rule/{category} ## show rules associated with a category
  • GET /rule/{uuid} ## show a specific rule by idenified by UUID

Payloads

POST /validate

The payload is an array of Products to be evaluated

[ product (, ...)]

where a product is defined as an object having required properties

  • product_id - integer
  • category - integer
  • attributes - an object containing named attributes

An attributes object typically contains one or more named objects to describe a single quality of a product.

When an attribute is references in a script, it is the value field which is evaluated. name,description,unit are all optional.

    ...
   "attributes": {
     "powered": {
        "name": "powered",
        "description": "Is Electrically Powered",
        "value": true,
        "unit": null
     }

Other properties may optionally be present on the Product object and may be referenced by rules if so required.

A partial example:

[{
  "product_id": 1,
  "name": "Faucet 1",
  "description": "Test Faucet",
  "date_added": "today",
  "make": "Moen",
  "model": "T44s",
  "factory_id": "Mt44s",
  "brand": "Moen",
  "category": 1,
  "attributes": {
     "powered": {
        "name": "powered",
        "description": "Is Electrically Powered",
        "value": true,
        "unit": null
     },
     "power-voltage": {
        "name": "voltage",
        "description": "Voltage",
        "value": 12,
        "unit": null
     },
     "power-ampheres": {
        "name": "amps",
        "description": "amps",
        "value": 0.5,
        "unit": null
...

The file items3.json represents the best complete example.

POST /rule ## create a rule

To create a new rule, the payload is as specified below.

The specified categories to related the rule to must already exist.

{
   "rule": {
      "description": "wall mounted sinks need carriers",
      "ruleGroup": "vb1",
      "weight": 1,
      "expr": "if (sink.mounting == 'wall'){\r\n\trequire {carrier}\r\n}\r\nreturn true"
   },
   "categories": [ "sink" ]
}

POST /categories ## create one or more categories

To create one or more categories, the payload is as specified below.

The specified categories must not already exist.

[ 'sink','faucet']

PUT /rule/{uuid} ## update an existing rule

The specified categories to related the rule to must already exist.

{
   "rule": {
      "description": "wall mounted sinks need carriers",
      "ruleGroup": "vb1",
      "weight": 1,
      "expr": "if (sink.mounting == 'wall'){\r\n\trequire {carrier}\r\n}\r\nreturn true"
   },
   "categories": [ "sink" ]
}

JMX

All implemented endpoints publish detailed timing information as JMX mBeans