From 3b71c9772e0f86b5cef9840668a36569540a037c Mon Sep 17 00:00:00 2001 From: ramverma Date: Wed, 10 Jul 2019 11:25:37 +0000 Subject: Adding apex-pdp & distribution documents to parent Change-Id: If8039f68a1669787685d4a7bdc37dec203901062 Issue-ID: POLICY-1898 Signed-off-by: ramverma --- docs/apex/APEX-User-Manual.rst | 8071 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 8071 insertions(+) create mode 100644 docs/apex/APEX-User-Manual.rst (limited to 'docs/apex/APEX-User-Manual.rst') diff --git a/docs/apex/APEX-User-Manual.rst b/docs/apex/APEX-User-Manual.rst new file mode 100644 index 00000000..01f74fab --- /dev/null +++ b/docs/apex/APEX-User-Manual.rst @@ -0,0 +1,8071 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. http://creativecommons.org/licenses/by/4.0 + + +APEX User Manual +**************** + +.. contents:: + :depth: 3 + +Installation +^^^^^^^^^^^^ + +Requirements +------------ + + .. container:: paragraph + + APEX is 100% written in Java and runs on any platform + that supports a JVM, e.g. Windows, Unix, Cygwin. Some + APEX applications (such as the monitoring application) + come as web archives, they do require a war-capable web + server installed. + +Installation Requirements +######################### + + .. container:: ulist + + - Downloaded distribution: JAVA runtime environment + (JRE, Java 8 or later, APEX is tested with the + Oracle Java) + + - Building from source: JAVA development kit (JDK, + Java 8 or later, APEX is tested with the Oracle + Java) + + - A web archive capable webserver, for instance for + the monitoring application + + .. container:: ulist + + - for instance `Apache + Tomcat `__ + + - Sufficient rights to install APEX on the system + + - Installation tools depending on the installation + method used: + + .. container:: ulist + + - ZIP to extract from a ZIP distribution + + .. container:: ulist + + - Windows for instance + `7Zip `__ + + - TAR and GZ to extract from that TAR.GZ + distribution + + .. container:: ulist + + - Windows for instance + `7Zip `__ + + - RPM to install from the RPM distribution + + .. container:: ulist + + - Install: ``sudo apt-get install rpm`` + + - DPKG to install from the DEB distribution + + .. container:: ulist + + - Install: ``sudo apt-get install dpkg`` + +Feature Requirements +#################### + + .. container:: paragraph + + APEX supports a number of features that require extra + software being installed. + + .. container:: ulist + + - `Apache Kafka `__ to + connect APEX to a Kafka message bus + + - `Hazelcast `__ to use + distributed hash maps for context + + - `Infinispan `__ for + distributed context and persistence + + - `Docker `__ to run APEX + inside a Docker container + +Build (Install from Source) Requirements +######################################## + + .. container:: paragraph + + Installation from source requires a few development + tools + + .. container:: ulist + + - GIT to retrieve the source code + + - Java SDK, Java version 8 or later + + - Apache Maven 3 (the APEX build environment) + +Get the APEX Source Code +------------------------ + + .. container:: paragraph + + The first APEX source code was hosted on Github in + January 2018. By the end of 2018, APEX was added as a + project in the ONAP Policy Framework, released later in + the ONAP Casablanca release. + + .. container:: paragraph + + The APEX source code is hosted in ONAP as project APEX. + The current stable version is in the master branch. + Simply clone the master branch from ONAP using HTTPS. + + .. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + git clone https://gerrit.onap.org/r/policy/apex-pdp + +Build APEX +---------- + + .. container:: paragraph + + The examples in this document assume that the APEX source + repositories are cloned to: + + .. container:: ulist + + - Unix, Cygwin: ``/usr/local/src/apex-pdp`` + + - Windows: ``C:\dev\apex-pdp`` + + - Cygwin: ``/cygdrive/c/dev/apex-pdp`` + + .. important:: + A Build requires ONAP Nexus + APEX has a dependency to ONAP parent projects. You might need to adjust your Maven M2 settings. The most current + settings can be found in the ONAP oparent repo: `Settings `__. + + .. important:: + A Build needs Space + Building APEX requires approximately 2-3 GB of hard disc space, 1 GB for the actual build with full + distribution and 1-2 GB for the downloaded dependencies + + .. important:: + A Build requires Internet (for first build) + During the build, several (a lot) of Maven dependencies will be downloaded and stored in the configured local Maven + repository. The first standard build (and any first specific build) requires Internet access to download those + dependencies. + + .. important:: + Building RPM distributions + RPM images are only build if the ``rpm`` package is installed (Unix). To install ``rpm`` run ``sudo apt-get install rpm``, + then build APEX. + + .. container:: paragraph + + Use Maven to for a standard build without any tests. + + +-------------------------------------------------------+--------------------------------------------------------+ + | Unix, Cygwin | Windows | + +=======================================================+========================================================+ + | .. container:: | .. container:: | + | | | + | .. container:: content | .. container:: content | + | | | + | .. code:: | .. code:: | + | :number-lines: | :number-lines: | + | | | + | # cd /usr/local/src/apex-pdp | >c: | + | # mvn clean install -DskipTest | >cd \dev\apex | + | | >mvn clean install -DskipTests | + +-------------------------------------------------------+--------------------------------------------------------+ + +.. container:: paragraph + + The build takes 2-3 minutes on a standard development laptop. It + should run through without errors, but with a lot of messages from + the build process. + +.. container:: paragraph + + When Maven is finished with the build, the final screen should look + similar to this (omitting some ``success`` lines): + +.. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + [INFO] tools .............................................. SUCCESS [ 0.248 s] + [INFO] tools-common ....................................... SUCCESS [ 0.784 s] + [INFO] simple-wsclient .................................... SUCCESS [ 3.303 s] + [INFO] model-generator .................................... SUCCESS [ 0.644 s] + [INFO] packages ........................................... SUCCESS [ 0.336 s] + [INFO] apex-pdp-package-full .............................. SUCCESS [01:10 min] + [INFO] Policy APEX PDP - Docker build 2.0.0-SNAPSHOT ...... SUCCESS [ 10.307 s] + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 03:43 min + [INFO] Finished at: 2018-09-03T11:56:01+01:00 + [INFO] ------------------------------------------------------------------------ + +.. container:: paragraph + + The build will have created all artifacts required for an APEX + installation. The following example show how to change to the target + directory and how it should look like. + ++----------------------------------------------------------------------------------------------------------------------------+ +| Unix, Cygwin | ++============================================================================================================================+ +| .. container:: | +| | +| .. container:: listingblock | +| | +| .. container:: content | +| | +| .. code:: | +| :number-lines: | +| | +| -rwxrwx---+ 1 esvevan Domain Users 772 Sep 3 11:55 apex-pdp-package-full_2.0.0~SNAPSHOT_all.changes* | +| -rwxrwx---+ 1 esvevan Domain Users 146328082 Sep 3 11:55 apex-pdp-package-full-2.0.0-SNAPSHOT.deb* | +| -rwxrwx---+ 1 esvevan Domain Users 15633 Sep 3 11:54 apex-pdp-package-full-2.0.0-SNAPSHOT.jar* | +| -rwxrwx---+ 1 esvevan Domain Users 146296819 Sep 3 11:55 apex-pdp-package-full-2.0.0-SNAPSHOT-tarball.tar.gz* | +| drwxrwx---+ 1 esvevan Domain Users 0 Sep 3 11:54 archive-tmp/ | +| -rwxrwx---+ 1 esvevan Domain Users 89 Sep 3 11:54 checkstyle-cachefile* | +| -rwxrwx---+ 1 esvevan Domain Users 10621 Sep 3 11:54 checkstyle-checker.xml* | +| -rwxrwx---+ 1 esvevan Domain Users 584 Sep 3 11:54 checkstyle-header.txt* | +| -rwxrwx---+ 1 esvevan Domain Users 86 Sep 3 11:54 checkstyle-result.xml* | +| drwxrwx---+ 1 esvevan Domain Users 0 Sep 3 11:54 classes/ | +| drwxrwx---+ 1 esvevan Domain Users 0 Sep 3 11:54 dependency-maven-plugin-markers/ | +| drwxrwx---+ 1 esvevan Domain Users 0 Sep 3 11:54 etc/ | +| drwxrwx---+ 1 esvevan Domain Users 0 Sep 3 11:54 examples/ | +| drwxrwx---+ 1 esvevan Domain Users 0 Sep 3 11:55 install_hierarchy/ | +| drwxrwx---+ 1 esvevan Domain Users 0 Sep 3 11:54 maven-archiver/ | ++----------------------------------------------------------------------------------------------------------------------------+ + ++--------------------------------------------------------------------------------------------------------+ +| Windows | ++========================================================================================================+ +| .. container:: | +| | +| .. container:: listingblock | +| | +| .. container:: content | +| | +| .. code:: | +| :number-lines: | +| | +| 03/09/2018 11:55 . | +| 03/09/2018 11:55 .. | +| 03/09/2018 11:55 146,296,819 apex-pdp-package-full-2.0.0-SNAPSHOT-tarball.tar.gz | +| 03/09/2018 11:55 146,328,082 apex-pdp-package-full-2.0.0-SNAPSHOT.deb | +| 03/09/2018 11:54 15,633 apex-pdp-package-full-2.0.0-SNAPSHOT.jar | +| 03/09/2018 11:55 772 apex-pdp-package-full_2.0.0~SNAPSHOT_all.changes | +| 03/09/2018 11:54 archive-tmp | +| 03/09/2018 11:54 89 checkstyle-cachefile | +| 03/09/2018 11:54 10,621 checkstyle-checker.xml | +| 03/09/2018 11:54 584 checkstyle-header.txt | +| 03/09/2018 11:54 86 checkstyle-result.xml | +| 03/09/2018 11:54 classes | +| 03/09/2018 11:54 dependency-maven-plugin-markers | +| 03/09/2018 11:54 etc | +| 03/09/2018 11:54 examples | +| 03/09/2018 11:55 install_hierarchy | +| 03/09/2018 11:54 maven-archiver | +| 8 File(s) 292,652,686 bytes | +| 9 Dir(s) 14,138,720,256 bytes free | ++--------------------------------------------------------------------------------------------------------+ + +Install APEX +------------ + + .. container:: paragraph + + APEX can be installed in different ways: + + .. container:: ulist + + - Unix: automatically using ``rpm`` or ``dpkg`` from ``.rpm`` or + ``.deb`` archive + + - Windows, Unix, Cygwin: manually from a ``.tar.gz`` archive + + - Windows, Unix, Cygwin: build from source using Maven, then + install manually + +Install with RPM and DPKG +######################### + + .. container:: paragraph + + The install distributions of APEX automatically install the + system. The installation directory is + ``/opt/app/policy/apex-pdp``. Log files are located in + ``/var/log/onap/policy/apex-pdp``. The latest APEX version will + be available as ``/opt/app/policy/apex-pdp/apex-pdp``. + + .. container:: paragraph + + For the installation, a new user ``apexuser`` and a new group + ``apexuser`` will be created. This user owns the installation + directories and the log file location. The user is also used by + the standard APEX start scripts to run APEX with this user’s + permissions. + + +-----------------------------------------------------------------------+ + | RPM Installation | + +=======================================================================+ + | .. container:: | + | | + | .. container:: listingblock | + | | + | .. container:: content | + | | + | .. code:: | + | :number-lines: | + | | + | # sudo rpm -i apex-pdp-package-full-2.0.0-SNAPSHOT.rpm | + | ********************preinst******************* | + | arguments 1 | + | ********************************************** | + | creating group apexuser . . . | + | creating user apexuser . . . | + | ********************postinst**************** | + | arguments 1 | + | *********************************************** | + +-----------------------------------------------------------------------+ + ++--------------------------------------------------------------------------------------+ +| DPKG Installation | ++======================================================================================+ +| .. container:: | +| | +| .. container:: listingblock | +| | +| .. container:: content | +| | +| .. code:: | +| :number-lines: | +| | +| # sudo dpkg -i apex-pdp-package-full-2.0.0-SNAPSHOT.deb | +| Selecting previously unselected package apex-uservice. | +| (Reading database ... 288458 files and directories currently installed.) | +| Preparing to unpack apex-pdp-package-full-2.0.0-SNAPSHOT.deb ... | +| ********************preinst******************* | +| arguments install | +| ********************************************** | +| creating group apexuser . . . | +| creating user apexuser . . . | +| Unpacking apex-uservice (2.0.0-SNAPSHOT) ... | +| Setting up apex-uservice (2.0.0-SNAPSHOT) ... | +| ********************postinst**************** | +| arguments configure | +| *********************************************** | ++--------------------------------------------------------------------------------------+ + +.. container:: paragraph + + Once the installation is finished, APEX is fully installed and ready + to run. + +Install Manually from Archive (Unix, Cygwin) +############################################ + + .. container:: paragraph + + Download a ``tar.gz`` archive. Create a directory where APEX + should be installed. Extract the ``tar`` archive. The following + example shows how to install APEX in ``/opt/apex`` and create a + link to ``/opt/apex/apex`` for the most recent installation. + + .. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + # cd /opt + # mkdir apex + # cd apex + # mkdir apex-full-2.0.0-SNAPSHOT + # tar xvfz ~/Downloads/apex-pdp-package-full-2.0.0-SNAPSHOT.tar.gz -C apex-full-2.0.0-SNAPSHOT + # ln -s apex apex-pdp-package-full-2.0.0-SNAPSHOT + +Install Manually from Archive (Windows, 7Zip, GUI) +################################################## + + .. container:: paragraph + + Download a ``tar.gz`` archive and copy the file into the install + folder (in this example ``C:\apex``). Assuming you are using 7Zip, + right click on the file and extract the ``tar`` archive. Note: the + screenshots might show an older version than you have. + + .. container:: imageblock + + .. container:: content + + |Extract the TAR archive| + + .. container:: paragraph + + The right-click on the new created TAR file and extract the actual + APEX distribution. + + .. container:: imageblock + + .. container:: content + + |Extract the APEX distribution| + + .. container:: paragraph + + Inside the new APEX folder you see the main directories: ``bin``, + ``etc``, ``examples``, ``lib``, and ``war`` + + .. container:: paragraph + + Once extracted, please rename the created folder to + ``apex-full-2.0.0-SNAPSHOT``. This will keep the directory name in + line with the rest of this documentation. + +Install Manually from Archive (Windows, 7Zip, CMD) +################################################## + + .. container:: paragraph + + Download a ``tar.gz`` archive and copy the file into the install + folder (in this example ``C:\apex``). Start ``cmd``, for instance + typing ``Windows+R`` and then ``cmd`` in the dialog. Assuming + ``7Zip`` is installed in the standard folder, simply run the + following commands (for APEX version 2.0.0-SNAPSHOT full + distribution) + + .. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + >c: + >cd \apex + >"\Program Files\7-Zip\7z.exe" x apex-pdp-package-full-2.0.0-SNAPSHOT.tar.gz -so | "\Program Files\7-Zip\7z.exe" x -aoa -si -ttar -o"apex-full-2.0.0-SNAPSHOT" + +.. container:: paragraph + + APEX is now installed in the folder + ``C:\apex\apex-full-2.0.0-SNAPSHOT``. + +Build from Source +----------------- + +Build and Install Manually (Unix, Windows, Cygwin) +################################################## + + .. container:: paragraph + + Clone the APEX GIT repositories into a directory. Go to that + directory. Use Maven to build APEX (all details on building + APEX from source can be found in *APEX HowTo: Build*). Install + from the created artifacts (``rpm``, ``deb``, ``tar.gz``, or + copying manually). + + .. important:: + Building RPM distributions + RPM images are only build if the ``rpm`` package is installed (Unix). To install ``rpm`` run + ``sudo apt-get install rpm``, then build APEX. + + .. container:: paragraph + + The following example shows how to build the APEX system, + without tests (``-DskipTests``) to safe some time. It assumes + that the APX GIT repositories are cloned to: + + .. container:: ulist + + - Unix, Cygwin: ``/usr/local/src/apex`` + + - Windows: ``C:\dev\apex`` + + +-------------------------------------------------------+--------------------------------------------------------+ + | Unix, Cygwin | Windows | + +=======================================================+========================================================+ + | .. container:: | .. container:: | + | | | + | .. container:: content | .. container:: content | + | | | + | .. code:: | .. code:: | + | :number-lines: | :number-lines: | + | | | + | # cd /usr/local/src/apex | >c: | + | # mvn clean install -DskipTests | >cd \dev\apex | + | | >mvn clean install -DskipTests | + +-------------------------------------------------------+--------------------------------------------------------+ + +.. container:: paragraph + + The build takes about 2 minutes without test and about 4-5 minutes + with tests on a standard development laptop. It should run through + without errors, but with a lot of messages from the build process. If + build with tests (i.e. without ``-DskipTests``), there will be error + messages and stack trace prints from some tests. This is normal, as + long as the build finishes successful. + +.. container:: paragraph + + When Maven is finished with the build, the final screen should look + similar to this (omitting some ``success`` lines): + +.. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + [INFO] tools .............................................. SUCCESS [ 0.248 s] + [INFO] tools-common ....................................... SUCCESS [ 0.784 s] + [INFO] simple-wsclient .................................... SUCCESS [ 3.303 s] + [INFO] model-generator .................................... SUCCESS [ 0.644 s] + [INFO] packages ........................................... SUCCESS [ 0.336 s] + [INFO] apex-pdp-package-full .............................. SUCCESS [01:10 min] + [INFO] Policy APEX PDP - Docker build 2.0.0-SNAPSHOT ...... SUCCESS [ 10.307 s] + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + [INFO] Total time: 03:43 min + [INFO] Finished at: 2018-09-03T11:56:01+01:00 + [INFO] ------------------------------------------------------------------------ + +.. container:: paragraph + + The build will have created all artifacts required for an APEX + installation. The following example show how to change to the target + directory and how it should look like. + ++-----------------------------------------------------------------------------------------------------------------------------+ +| Unix, Cygwin | ++=============================================================================================================================+ +| .. container:: | +| | +| .. container:: listingblock | +| | +| .. code:: | +| :number-lines: | +| | +| # cd packages/apex-pdp-package-full/target | +| # ls -l | +| -rwxrwx---+ 1 esvevan Domain Users 772 Sep 3 11:55 apex-pdp-package-full_2.0.0~SNAPSHOT_all.changes* | +| -rwxrwx---+ 1 esvevan Domain Users 146328082 Sep 3 11:55 apex-pdp-package-full-2.0.0-SNAPSHOT.deb* | +| -rwxrwx---+ 1 esvevan Domain Users 15633 Sep 3 11:54 apex-pdp-package-full-2.0.0-SNAPSHOT.jar* | +| -rwxrwx---+ 1 esvevan Domain Users 146296819 Sep 3 11:55 apex-pdp-package-full-2.0.0-SNAPSHOT-tarball.tar.gz* | +| drwxrwx---+ 1 esvevan Domain Users 0 Sep 3 11:54 archive-tmp/ | +| -rwxrwx---+ 1 esvevan Domain Users 89 Sep 3 11:54 checkstyle-cachefile* | +| -rwxrwx---+ 1 esvevan Domain Users 10621 Sep 3 11:54 checkstyle-checker.xml* | +| -rwxrwx---+ 1 esvevan Domain Users 584 Sep 3 11:54 checkstyle-header.txt* | +| -rwxrwx---+ 1 esvevan Domain Users 86 Sep 3 11:54 checkstyle-result.xml* | +| drwxrwx---+ 1 esvevan Domain Users 0 Sep 3 11:54 classes/ | +| drwxrwx---+ 1 esvevan Domain Users 0 Sep 3 11:54 dependency-maven-plugin-markers/ | +| drwxrwx---+ 1 esvevan Domain Users 0 Sep 3 11:54 etc/ | +| drwxrwx---+ 1 esvevan Domain Users 0 Sep 3 11:54 examples/ | +| drwxrwx---+ 1 esvevan Domain Users 0 Sep 3 11:55 install_hierarchy/ | +| drwxrwx---+ 1 esvevan Domain Users 0 Sep 3 11:54 maven-archiver/ | ++-----------------------------------------------------------------------------------------------------------------------------+ + ++-----------------------------------------------------------------------------------------------------------------------------+ +| Windows | ++=============================================================================================================================+ +| .. container:: | +| | +| .. container:: listingblock | +| | +| .. code:: | +| :number-lines: | +| | +| >cd packages\apex-pdp-package-full\target | +| >dir | +| 03/09/2018 11:55 . | +| 03/09/2018 11:55 .. | +| 03/09/2018 11:55 146,296,819 apex-pdp-package-full-2.0.0-SNAPSHOT-tarball.tar.gz | +| 03/09/2018 11:55 146,328,082 apex-pdp-package-full-2.0.0-SNAPSHOT.deb | +| 03/09/2018 11:54 15,633 apex-pdp-package-full-2.0.0-SNAPSHOT.jar | +| 03/09/2018 11:55 772 apex-pdp-package-full_2.0.0~SNAPSHOT_all.changes | +| 03/09/2018 11:54 archive-tmp | +| 03/09/2018 11:54 89 checkstyle-cachefile | +| 03/09/2018 11:54 10,621 checkstyle-checker.xml | +| 03/09/2018 11:54 584 checkstyle-header.txt | +| 03/09/2018 11:54 86 checkstyle-result.xml | +| 03/09/2018 11:54 classes | +| 03/09/2018 11:54 dependency-maven-plugin-markers | +| 03/09/2018 11:54 etc | +| 03/09/2018 11:54 examples | +| 03/09/2018 11:55 install_hierarchy | +| 03/09/2018 11:54 maven-archiver | +| 8 File(s) 292,652,686 bytes | +| 9 Dir(s) 14,138,720,256 bytes free | ++-----------------------------------------------------------------------------------------------------------------------------+ + +.. container:: paragraph + + Now, take the ``.deb`` or the ``.tar.gz`` file and install APEX. + Alternatively, copy the content of the folder ``install_hierarchy`` + to your APEX directory. + +Installation Layout +------------------- + + .. container:: paragraph + + A full installation of APEX comes with the following layout. + + .. container:: listingblock + + .. container:: content + + :: + + $APEX_HOME + ├───bin (1) + ├───etc (2) + │ ├───editor + │ ├───hazelcast + │ ├───infinispan + │ └───META-INF + ├───examples (3) + │ ├───config (4) + │ ├───docker (5) + │ ├───events (6) + │ ├───html (7) + │ ├───models (8) + │ └───scripts (9) + ├───lib (10) + │ └───applications (11) + └───war (12) + + .. container:: colist arabic + + +-----------------------------------+-----------------------------------+ + | **1** | binaries, mainly scripts (bash | + | | and bat) to start the APEX engine | + | | and applications | + +-----------------------------------+-----------------------------------+ + | **2** | configuration files, such as | + | | logback (logging) and third party | + | | library configurations | + +-----------------------------------+-----------------------------------+ + | **3** | example policy models to get | + | | started | + +-----------------------------------+-----------------------------------+ + | **4** | configurations for the examples | + | | (with sub directories for | + | | individual examples) | + +-----------------------------------+-----------------------------------+ + | **5** | Docker files and additional | + | | Docker instructions for the | + | | exampples | + +-----------------------------------+-----------------------------------+ + | **6** | example events for the examples | + | | (with sub directories for | + | | individual examples) | + +-----------------------------------+-----------------------------------+ + | **7** | HTML files for some examples, | + | | e.g. the Decisionmaker example | + +-----------------------------------+-----------------------------------+ + | **8** | the policy models, generated for | + | | each example (with sub | + | | directories for individual | + | | examples) | + +-----------------------------------+-----------------------------------+ + | **9** | additional scripts for the | + | | examples (with sub directories | + | | for individual examples) | + +-----------------------------------+-----------------------------------+ + | **10** | the library folder with all Java | + | | JAR files | + +-----------------------------------+-----------------------------------+ + | **11** | applications, also known as jar | + | | with dependencies (or fat jars), | + | | individually deployable | + +-----------------------------------+-----------------------------------+ + | **12** | WAR files for web applications | + +-----------------------------------+-----------------------------------+ + +System Configuration +-------------------- + + .. container:: paragraph + + Once APEX is installed, a few configurations need to be done: + + .. container:: ulist + + - Create an APEX user and an APEX group (optional, if not + installed using RPM and DPKG) + + - Create environment settings for ``APEX_HOME`` and + ``APEX_USER``, required by the start scripts + + - Change settings of the logging framework (optional) + + - Create directories for logging, required (execution might fail + if directories do not exist or cannot be created) + +APEX User and Group +################### + + .. container:: paragraph + + On smaller installations and test systems, APEX can run as any + user or group. + + .. container:: paragraph + + However, if APEX is installed in production, we strongly + recommend you set up a dedicated user for running APEX. This + will isolate the execution of APEX to that user. We recommend + you use the userid ``apexuser`` but you may use any user you + choose. + + .. container:: paragraph + + The following example, for UNIX, creates a group called + ``apexuser``, an APEX user called ``apexuser``, adds the group + to the user, and changes ownership of the APEX installation to + the user. Substitute ```` with the directory where + APEX is installed. + + .. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + # sudo groupadd apexuser + # sudo useradd -g apexuser apexuser + # sudo chown -R apexuser:apexuser + +.. container:: paragraph + + For other operating systems please consult your manual or system + administrator. + +Environment Settings: APEX_HOME and APEX_USER +############################################# + + .. container:: paragraph + + The provided start scripts for APEX require two environment + variables being set: + + .. container:: ulist + + - ``APEX_USER`` with the user under whos name and permission APEX + should be started (Unix only) + + - ``APEX_HOME`` with the directory where APEX is installed (Unix, + Windows, Cygwin) + + .. container:: paragraph + + The first row in the following table shows how to set these + environment variables temporary (assuming the user is + ``apexuser``). The second row shows how to verify the settings. + The last row explains how to set those variables permanently. + + +------------------------------------------------+---------------------------------------------------------+ + | Unix, Cygwin (bash/tcsh) | Windows | + +================================================+=========================================================+ + | .. container:: | .. container:: | + | | | + | .. container:: content | .. container:: content | + | | | + | .. code:: | .. code:: | + | :number-lines: | :number-lines: | + | | | + | # export APEX_USER=apexuser | >set APEX_HOME=C:\apex\apex-full-2.0.0-SNAPSHOT | + | # cd /opt/app/policy/apex-pdp | | + | # export APEX_HOME=`pwd` | | + | | | + +------------------------------------------------+ | + | .. container:: | | + | | | + | .. container:: content | | + | | | + | .. code:: tcsh | | + | :number-lines: | | + | | | + | # setenv APEX_USER apexuser | | + | # cd /opt/app/policy/apex-pdp | | + | # setenv APEX_HOME `pwd` | | + | | | + +------------------------------------------------+---------------------------------------------------------+ + | .. container:: | .. container:: | + | | | + | .. container:: content | .. container:: content | + | | | + | .. code:: | .. code:: | + | :number-lines: | :number-lines: | + | | | + | # env | grep APEX | >set APEX_HOME | + | # APEX_USER=apexuser | APEX_HOME=\apex\apex-full-2.0.0-SNAPSHOT | + | # APEX_HOME=/opt/app/policy/apex-pdp | | + | | | + +------------------------------------------------+---------------------------------------------------------+ + +Making Environment Settings Permanent (Unix, Cygwin) +==================================================== + + .. container:: paragraph + + For a per-user setting, edit the a user’s ``bash`` or ``tcsh`` + settings in ``~/.bashrc`` or ``~/.tcshrc``. For system-wide + settings, edit ``/etc/profiles`` (requires permissions). + +Making Environment Settings Permanent (Windows) +=============================================== + + .. container:: paragraph + + On Windows 7 do + + .. container:: ulist + + - Click on the **Start** Menu + + - Right click on **Computer** + + - Select **Properties** + + .. container:: paragraph + + On Windows 8/10 do + + .. container:: ulist + + - Click on the **Start** Menu + + - Select **System** + + .. container:: paragraph + + Then do the following + + .. container:: ulist + + - Select **Advanced System Settings** + + - On the **Advanced** tab, click the **Environment Variables** + button + + - Edit an existing variable, or create a new System variable: + 'Variable name'="APEX_HOME", 'Variable + value'="C:\apex\apex-full-2.0.0-SNAPSHOT" + + .. container:: paragraph + + For the settings to take effect, an application needs to be + restarted (e.g. any open ``cmd`` window). + +Edit the APEX Logging Settings +############################## + + .. container:: paragraph + + Configure the APEX logging settings to your requirements, for + instance: + + .. container:: ulist + + - change the directory where logs are written to, or + + - change the log levels + + .. container:: paragraph + + Edit the file ``$APEX_HOME/etc/logback.xml`` for any required + changes. To change the log directory change the line + + .. container:: paragraph + + ```` + + .. container:: paragraph + + to + + .. container:: paragraph + + ```` + + .. container:: paragraph + + On Windows, it is recommended to change the log directory to: + + .. container:: paragraph + + ```` + + .. container:: paragraph + + Note: Be careful about when to use ``\`` vs. ``/`` as the path + separator! + +Create Directories for Logging +############################## + + .. container:: paragraph + + Make sure that the log directory exists. This is important when + APEX was installed manually or when the log directory was changed + in the settings (see above). + + +------------------------------------------------------------------+-------------------------------------------------------+ + | Unix, Cygwin | Windows | + +==================================================================+=======================================================+ + | .. container:: | .. container:: | + | | | + | .. container:: content | .. container:: content | + | | | + | .. code:: | .. code:: | + | :number-lines: | :number-lines: | + | | | + | mkdir -p /var/log/onap/policy/apex-pdp | >mkdir C:\apex\apex-full-2.0.0-SNAPSHOT\logs | + | chown -R apexuser:apexuser /var/log/onap/policy/apex-pdp | | + +------------------------------------------------------------------+-------------------------------------------------------+ + +Verify the APEX Installation +---------------------------- + + .. container:: paragraph + + When APEX is installed and all settings are realized, the + installation can be verified. + +Verify Installation - run Engine +################################ + + .. container:: paragraph + + A simple verification of an APEX installation can be done by + simply starting the APEX engine without any configuration. On + Unix (or Cygwin) start the engine using + ``$APEX_HOME/bin/apexEngine.sh``. On Windows start the engine + using ``%APEX_HOME%\bin\apexEngine.bat``. The engine will fail + to fully start. However, if the output looks similar to the + following line, the APEX installation is realized. + + .. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + Starting Apex service with parameters [] . . . + start of Apex service failed: Apex configuration file was not specified as an argument + 2018-09-03 13:11:33,914 Apex [main] ERROR o.o.p.a.service.engine.main.ApexMain - start of Apex service failed + org.onap.policy.apex.model.basicmodel.concepts.ApexException: Apex configuration file was not specified as an argument + at org.onap.policy.apex.service.engine.main.ApexCommandLineArguments.validateReadableFile(ApexCommandLineArguments.java:267) + at org.onap.policy.apex.service.engine.main.ApexCommandLineArguments.validate(ApexCommandLineArguments.java:161) + at org.onap.policy.apex.service.engine.main.ApexMain.(ApexMain.java:68) + at org.onap.policy.apex.service.engine.main.ApexMain.main(ApexMain.java:165) + usage: org.onap.policy.apex.service.engine.main.ApexMain [options...] + options + -c,--config-file the full path to the configuration file to use, the configuration file must be a Json file + containing the Apex configuration parameters + -h,--help outputs the usage of this command + -m,--model-file the full path to the model file to use, if set it overrides the model file set in the + configuration file + -v,--version outputs the version of Apex + +Verify Installation - run an Example +#################################### + + .. container:: paragraph + + A full APEX installation comes with several examples. Here, we can + fully verify the installation by running one of the examples. + + .. container:: paragraph + + We use the example called *SampleDomain* and configure the engine + to use standard in and standard out for events. Run the engine + with the provided configuration. Note: Cygwin executes scripts as + Unix scripts but runs Java as a Windows application, thus the + configuration file must be given as a Windows path. + + .. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + # $APEX_HOME/bin/apexEngine.sh -c $APEX_HOME/examples/config/SampleDomain/Stdin2StdoutJsonEventJava.json (1) + # $APEX_HOME/bin/apexEngine.sh -c C:/apex/apex-full-2.0.0-SNAPSHOT/examples/config/SampleDomain/Stdin2StdoutJsonEventJava.json (2) + >%APEX_HOME%\bin\apexEngine.bat -c %APEX_HOME%\examples\config\SampleDomain\Stdin2StdoutJsonEventJava.json :: (3) + +.. container:: colist arabic + + +-------+---------+ + | **1** | UNIX | + +-------+---------+ + | **2** | Cygwin | + +-------+---------+ + | **3** | Windows | + +-------+---------+ + +.. container:: paragraph + + The engine should start successfully. Assuming the logging levels are + not change (default level is ``info``), the output should look + similar to this (last few lines) + +.. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + Starting Apex service with parameters [-c, v:/dev/ericsson/apex/onap/apex-pdp/packages/apex-pdp-package-full/target/install_hierarchy/examples/config/SampleDomain/Stdin2StdoutJsonEventJava.json] . . . + 2018-09-05 15:16:42,800 Apex [main] INFO o.o.p.a.s.e.r.impl.EngineServiceImpl - Created apex engine MyApexEngine-0:0.0.1 . + 2018-09-05 15:16:42,804 Apex [main] INFO o.o.p.a.s.e.r.impl.EngineServiceImpl - Created apex engine MyApexEngine-1:0.0.1 . + 2018-09-05 15:16:42,804 Apex [main] INFO o.o.p.a.s.e.r.impl.EngineServiceImpl - Created apex engine MyApexEngine-2:0.0.1 . + 2018-09-05 15:16:42,805 Apex [main] INFO o.o.p.a.s.e.r.impl.EngineServiceImpl - Created apex engine MyApexEngine-3:0.0.1 . + 2018-09-05 15:16:42,805 Apex [main] INFO o.o.p.a.s.e.r.impl.EngineServiceImpl - APEX service created. + 2018-09-05 15:16:43,962 Apex [main] INFO o.o.p.a.s.e.e.EngDepMessagingService - engine<-->deployment messaging starting . . . + 2018-09-05 15:16:43,963 Apex [main] INFO o.o.p.a.s.e.e.EngDepMessagingService - engine<-->deployment messaging started + 2018-09-05 15:16:44,987 Apex [main] INFO o.o.p.a.s.e.r.impl.EngineServiceImpl - Registering apex model on engine MyApexEngine-0:0.0.1 + 2018-09-05 15:16:45,112 Apex [main] INFO o.o.p.a.s.e.r.impl.EngineServiceImpl - Registering apex model on engine MyApexEngine-1:0.0.1 + 2018-09-05 15:16:45,113 Apex [main] INFO o.o.p.a.s.e.r.impl.EngineServiceImpl - Registering apex model on engine MyApexEngine-2:0.0.1 + 2018-09-05 15:16:45,113 Apex [main] INFO o.o.p.a.s.e.r.impl.EngineServiceImpl - Registering apex model on engine MyApexEngine-3:0.0.1 + 2018-09-05 15:16:45,120 Apex [main] INFO o.o.p.a.s.e.r.impl.EngineServiceImpl - Added the action listener to the engine + Started Apex service + +.. container:: paragraph + + Important are the last two line, stating that APEX has added the + final action listener to the engine and that the engine is started. + +.. container:: paragraph + + The engine is configured to read events from standard input and write + produced events to standard output. The policy model is a very simple + policy. + +.. container:: paragraph + + The following table shows an input event in the left column and an + output event in the right column. Past the input event into the + console where APEX is running, and the output event should appear in + the console. Pasting the input event multiple times will produce + output events with different values. + ++-------------------------------------------------------------+-------------------------------------------------------------+ +| Input Event | Example Output Event | ++=============================================================+=============================================================+ +| .. container:: | .. container:: | +| | | +| .. container:: content | .. container:: content | +| | | +| .. code:: | .. code:: | +| :number-lines: | :number-lines: | +| | | +| { | { | +| "nameSpace": "org.onap.policy.apex.sample.events", | "name": "Event0004", | +| "name": "Event0000", | "version": "0.0.1", | +| "version": "0.0.1", | "nameSpace": "org.onap.policy.apex.sample.events", | +| "source": "test", | "source": "Act", | +| "target": "apex", | "target": "Outside", | +| "TestSlogan": "Test slogan for External Event0", | "TestActCaseSelected": 2, | +| "TestMatchCase": 0, | "TestActStateTime": 1536157104627, | +| "TestTimestamp": 1469781869269, | "TestDecideCaseSelected": 0, | +| "TestTemperature": 9080.866 | "TestDecideStateTime": 1536157104625, | +| } | "TestEstablishCaseSelected": 0, | +| | "TestEstablishStateTime": 1536157104623, | +| | "TestMatchCase": 0, | +| | "TestMatchCaseSelected": 1, | +| | "TestMatchStateTime": 1536157104620, | +| | "TestSlogan": "Test slogan for External Event0", | +| | "TestTemperature": 9080.866, | +| | "TestTimestamp": 1469781869269 | +| | } | ++-------------------------------------------------------------+-------------------------------------------------------------+ + +.. container:: paragraph + + Terminate APEX by simply using ``CTRL+C`` in the console. + +Verify a Full Installation - REST Editor +######################################## + + .. container:: paragraph + + APEX has a REST application for viewing policy models. The + application can also be used to create new policy models close to + the engine native policy language. Start the REST editor as + follows. + + .. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + # $APEX_HOME/bin/apexApps.sh rest-editor + +.. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + >%APEX_HOME%\bin\apexApps.bat rest-editor + +.. container:: paragraph + + The script will start a simple web server + (`Grizzly `__) and deploy a + ``war`` web archive in it. Once the editor is started, it will be + available on ``localhost:18989``. The last few line of the messages + should be: + +.. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + Apex Editor REST endpoint (ApexEditorMain: Config=[ApexEditorParameters: URI=http://localhost:18989/apexservices/, TTL=-1sec], State=READY) starting at http://localhost:18989/apexservices/ . . . + Sep 05, 2018 10:35:57 PM org.glassfish.grizzly.http.server.NetworkListener start + INFO: Started listener bound to [localhost:18989] + Sep 05, 2018 10:35:57 PM org.glassfish.grizzly.http.server.HttpServer start + INFO: [HttpServer] Started. + Apex Editor REST endpoint (ApexEditorMain: Config=[ApexEditorParameters: URI=http://localhost:18989/apexservices/, TTL=-1sec], State=RUNNING) started at http://localhost:18989/apexservices/ + +.. container:: paragraph + + Now open a browser (Firefox, Chrome, Opera, Internet Explorer) and + use the URL ``http://localhost:18989/``. This will connect the + browser to the started REST editor. The start screen should be as + follows. + +.. container:: imageblock + + .. container:: content + + |REST Editor Start Screen| + + .. container:: title + + Figure 1. REST Editor Start Screen + +.. container:: paragraph + + Now load a policy model by clicking the menu ``File`` and then + ``Open``. In the opened dialog, go to the directory where APEX is + installed, then ``examples``, ``models``, ``SampleDomain``, and there + select the file ``SamplePolicyModelJAVA.json``. This will load the + policy model used to verify the policy engine (see above). Once + loaded, the screen should look as follows. + +.. container:: imageblock + + .. container:: content + + |REST Editor with loaded SampleDomain Policy Model| + + .. container:: title + + Figure 2. REST Editor with loaded SampleDomain Policy Model + +.. container:: paragraph + + Now you can use the REST editor. To finish this verification, simply + terminate your browser (or the tab), and then use ``CTRL+C`` in the + console where you started the REST editor. + +Installing WAR Applications +--------------------------- + + .. container:: paragraph + + APEX comes with a set of WAR files. These are complete + applications that can be installed and run in an application + server. All of these applications are realized as servlets. You + can find the WAR applications in ``$APEX_HOME/war`` (UNIX, Cygwin) + or ``%APEX_HOME%\war`` (Windows). + + .. container:: paragraph + + Installing and using the WAR applications requires a web server + that can execute ``war`` web archives. We recommend to use `Apache + Tomcat `__, however other web servers + can be used as well. + + .. container:: paragraph + + Install Apache Tomcat including the ``Manager App``, see `V9.0 + Docs `__ + for details. Start the Tomcat service, or make sure that Tomcat is + running. + + .. container:: paragraph + + There are multiple ways to install the APEX WAR applications: + + .. container:: ulist + + - copy the ``.war`` file into the Tomcat ``webapps`` folder + + - use the Tomcat ``Manager App`` to deploy via the web interface + + - deploy using a REST call to Tomcat + + .. container:: paragraph + + For details on how to install ``war`` files please consult the + `Tomcat + Documentation `__ + or the `Manager App + HOW-TO `__. + Once you installed an APEX WAR application (and wait for + sufficient time for Tomcat to finalize the installation), open the + ``Manager App`` in Tomcat. You should see the APEX WAR application + being installed and running. + + .. container:: paragraph + + In case of errors, examine the log files in the Tomcat log + directory. In a conventional install, those log files are in the + logs directory where Tomcat is installed. + + .. container:: paragraph + + The current APEX version provides the following WAR applications: + + .. container:: ulist + + - client-deployment-2.0.0-SNAPSHOT.war - a client to deploy new + policy models to a running engine + + - client-editor-2.0.0-SNAPSHOT.war - the standard policy REST + editor GUI + + - client-monitoring-2.0.0-SNAPSHOT.war - a client for monitoring + a running APEX engine + + - client-full-2.0.0-SNAPSHOT.war - a full client with a + one-stop-access to deployment, monitoring, and REST editor + + - examples-servlet-2.0.0-SNAPSHOT.war - an example APEX servlet + +Running APEX in Docker +---------------------- + + .. container:: paragraph + + Since APEX is in ONAP, we provide a full virtualization + environment for the engine. + +Run in ONAP +########### + + .. container:: paragraph + + Running APEX from the ONAP docker repository only requires 2 + commands: + + .. container:: olist arabic + + #. Log into the ONAP docker repo + + .. container:: listingblock + + .. container:: content + + :: + + docker login -u docker -p docker nexus3.onap.org:10003 + + .. container:: olist arabic + + #. Run the APEX docker image + + .. container:: listingblock + + .. container:: content + + :: + + docker run -it --rm nexus3.onap.org:10003/onap/policy-apex-pdp:latest + +Build a Docker Image +#################### + + .. container:: paragraph + + Alternatively, one can use the Dockerfile defined in the Docker + package to build an image. + + .. container:: listingblock + + .. container:: title + + APEX Dockerfile + + .. container:: content + + .. code:: + :number-lines: + + # + # Docker file to build an image that runs APEX on Java 8 in Ubuntu + # + FROM ubuntu:16.04 + + RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y software-properties-common && \ + add-apt-repository ppa:openjdk-r/ppa -y && \ + apt-get update && \ + apt-get install -y openjdk-8-jdk + + # Create apex user and group + RUN groupadd apexuser + RUN useradd --create-home -g apexuser apexuser + + # Add Apex-specific directories and set ownership as the Apex admin user + RUN mkdir -p /opt/app/policy/apex-pdp + RUN mkdir -p /var/log/onap/policy/apex-pdp + RUN chown -R apexuser:apexuser /var/log/onap/policy/apex-pdp + + # Unpack the tarball + RUN mkdir /packages + COPY apex-pdp-package-full.tar.gz /packages + RUN tar xvfz /packages/apex-pdp-package-full.tar.gz --directory /opt/app/policy/apex-pdp + RUN rm /packages/apex-pdp-package-full.tar.gz + + # Ensure everything has the correct permissions + RUN find /opt/app -type d -perm 755 + RUN find /opt/app -type f -perm 644 + RUN chmod a+x /opt/app/policy/apex-pdp/bin/* + + # Copy examples to Apex user area + RUN cp -pr /opt/app/policy/apex-pdp/examples /home/apexuser + + RUN apt-get clean + + RUN chown -R apexuser:apexuser /home/apexuser/* + + USER apexuser + ENV PATH /opt/app/policy/apex-pdp/bin:$PATH + WORKDIR /home/apexuser + +APEX Configurations Explained +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Introduction to APEX Configuration +---------------------------------- + + .. container:: paragraph + + An APEX engine can be configured to use various combinations + of event input handlers, event output handlers, event + protocols, context handlers, and logic executors. The system + is build using a plugin architecture. Each configuration + option is realized by a plugin, which can be loaded and + configured when the engine is started. New plugins can be + added to the system at any time, though to benefit from a + new plugin an engine will need to be restarted. + + .. container:: imageblock + + .. container:: content + + |APEX Configuration Matrix| + + .. container:: title + + Figure 3. APEX Configuration Matrix + + .. container:: paragraph + + The APEX distribution already comes with a number of + plugins. The figure above shows the provided plugins. Any + combination of input, output, event protocol, context + handlers, and executors is possible. + +General Configuration Format +---------------------------- + + .. container:: paragraph + + The APEX configuration file is a JSON file containing a few + main blocks for different parts of the configuration. Each + block then holds the configuration details. The following + code shows the main blocks: + + .. container:: listingblock + + .. container:: content + + .. code:: + + { + "engineServiceParameters":{ + ... (1) + "engineParameters":{ (2) + "engineParameters":{...}, (3) + "contextParameters":{...} (4) + } + }, + "eventInputParameters":{ (5) + "input1":{ (6) + "carrierTechnologyParameters":{...}, + "eventProtocolParameters":{...} + }, + "input2":{...}, (7) + "carrierTechnologyParameters":{...}, + "eventProtocolParameters":{...} + }, + ... (8) + }, + "eventOutputParameters":{ (9) + "output1":{ (10) + "carrierTechnologyParameters":{...}, + "eventProtocolParameters":{...} + }, + "output2":{ (11) + "carrierTechnologyParameters":{...}, + "eventProtocolParameters":{...} + }, + ... (12) + } + } + + .. container:: colist arabic + + +-----------------------------------+-----------------------------------+ + | **1** | main engine configuration | + +-----------------------------------+-----------------------------------+ + | **2** | engine parameters for plugin | + | | configurations (execution | + | | environments and context | + | | handling) | + +-----------------------------------+-----------------------------------+ + | **3** | engine specific parameters, | + | | mainly for executor plugins | + +-----------------------------------+-----------------------------------+ + | **4** | context specific parameters, e.g. | + | | for context schemas, persistence, | + | | etc. | + +-----------------------------------+-----------------------------------+ + | **5** | configuration of the input | + | | interface | + +-----------------------------------+-----------------------------------+ + | **6** | an example input called | + | | ``input1`` with carrier | + | | technology and event protocol | + +-----------------------------------+-----------------------------------+ + | **7** | an example input called | + | | ``input2`` with carrier | + | | technology and event protocol | + +-----------------------------------+-----------------------------------+ + | **8** | any further input configuration | + +-----------------------------------+-----------------------------------+ + | **9** | configuration of the output | + | | interface | + +-----------------------------------+-----------------------------------+ + | **10** | an example output called | + | | ``output1`` with carrier | + | | technology and event protocol | + +-----------------------------------+-----------------------------------+ + | **11** | an example output called | + | | ``output2`` with carrier | + | | technology and event protocol | + +-----------------------------------+-----------------------------------+ + | **12** | any further output configuration | + +-----------------------------------+-----------------------------------+ + +Engine Service Parameters +------------------------- + + .. container:: paragraph + + The configuration provides a number of parameters to + configure the engine. An example configuration with + explanations of all options is shown below. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "engineServiceParameters" : { + "name" : "AADMApexEngine", (1) + "version" : "0.0.1", (2) + "id" : 45, (3) + "instanceCount" : 4, (4) + "deploymentPort" : 12345, (5) + "policyModelFileName" : "examples/models/VPN/VPNPolicyModelJava.json", (6) + "periodicEventPeriod": 1000, (7) + "engineParameters":{ (8) + "engineParameters":{...}, (9) + "contextParameters":{...} (10) + } + } + + .. container:: colist arabic + + +-----------------------------------+-----------------------------------+ + | **1** | a name for the engine. The engine | + | | name is used to create a key in a | + | | runtime engine. An name matching | + | | the following regular expression | + | | can be used here: | + | | ``[A-Za-z0-9\\-_\\.]+`` | + +-----------------------------------+-----------------------------------+ + | **2** | a version of the engine, use | + | | semantic versioning as explained | + | | here: `Semantic | + | | Versioning `_ | + | | _. | + | | This version is used in a runtime | + | | engine to create a version of the | + | | engine. For that reason, the | + | | version must match the following | + | | regular expression ``[A-Z0-9.]+`` | + +-----------------------------------+-----------------------------------+ + | **3** | a numeric identifier for the | + | | engine | + +-----------------------------------+-----------------------------------+ + | **4** | the number of threads (policy | + | | instances executed in parallel) | + | | the engine should use, use ``1`` | + | | for single threaded engines | + +-----------------------------------+-----------------------------------+ + | **5** | the port for the deployment | + | | Websocket connection to the | + | | engine | + +-----------------------------------+-----------------------------------+ + | **6** | the model file to load into the | + | | engine on startup (optional) | + +-----------------------------------+-----------------------------------+ + | **7** | an optional timer for periodic | + | | policies, in milliseconds (a | + | | defined periodic policy will be | + | | executed every ``X`` | + | | milliseconds), not used of not | + | | set or ``0`` | + +-----------------------------------+-----------------------------------+ + | **8** | engine parameters for plugin | + | | configurations (execution | + | | environments and context | + | | handling) | + +-----------------------------------+-----------------------------------+ + | **9** | engine specific parameters, | + | | mainly for executor plugins | + +-----------------------------------+-----------------------------------+ + | **10** | context specific parameters, e.g. | + | | for context schemas, persistence, | + | | etc. | + +-----------------------------------+-----------------------------------+ + + .. container:: paragraph + + The model file is optional, it can also be specified via + command line. In any case, make sure all execution and other + required plug-ins for the loaded model are loaded as + required. + +Input and Output Interfaces +--------------------------- + + .. container:: paragraph + + An APEX engine has two main interfaces: + + .. container:: ulist + + - An *input* interface to receive events: also known as + ingress interface or consumer, receiving (consuming) + events commonly named triggers, and + + - An *output* interface to publish produced events: also + known as egress interface or producer, sending + (publishing) events commonly named actions or action + events. + + .. container:: paragraph + + The input and output interface is configured in terms of + inputs and outputs, respectively. Each input and output is a + combination of a carrier technology and an event protocol. + Carrier technologies and event protocols are provided by + plugins, each with its own specific configuration. Most + carrier technologies can be configured for input as well as + output. Most event protocols can be used for all carrier + technologies. One exception is the JMS object event + protocol, which can only be used for the JMS carrier + technology. Some further restrictions apply (for instance + for carrier technologies using bi- or uni-directional + modes). + + .. container:: paragraph + + Input and output interface can be configured separately, in + isolation, with any number of carrier technologies. The + resulting general configuration options are: + + .. container:: ulist + + - Input interface with one or more inputs + + .. container:: ulist + + - each input with a carrier technology and an event + protocol + + - some inputs with optional synchronous mode + + - some event protocols with additional parameters + + - Output interface with one or more outputs + + .. container:: ulist + + - each output with a carrier technology and an event + encoding + + - some outputs with optional synchronous mode + + - some event protocols with additional parameters + + .. container:: paragraph + + The configuration for input and output is contained in + ``eventInputParameters`` and ``eventOutputParameters``, + respectively. Inside here, one can configure any number of + inputs and outputs. Each of them needs to have a unique + identifier (name), the content of the name is free form. The + example below shows a configuration for two inputs and two + outputs. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "eventInputParameters": { (1) + "FirstConsumer": { (2) + "carrierTechnologyParameters" : {...}, (3) + "eventProtocolParameters":{...}, (4) + ... (5) + }, + "SecondConsumer": { (6) + "carrierTechnologyParameters" : {...}, (7) + "eventProtocolParameters":{...}, (8) + ... (9) + }, + }, + "eventOutputParameters": { (10) + "FirstProducer": { (11) + "carrierTechnologyParameters":{...}, (12) + "eventProtocolParameters":{...}, (13) + ... (14) + }, + "SecondProducer": { (15) + "carrierTechnologyParameters":{...}, (16) + "eventProtocolParameters":{...}, (17) + ... (18) + } + } + + .. container:: colist arabic + + +--------+--------------------------------------------------------------------+ + | **1** | input interface configuration, APEX input plugins | + +--------+--------------------------------------------------------------------+ + | **2** | first input called ``FirstConsumer`` | + +--------+--------------------------------------------------------------------+ + | **3** | carrier technology for plugin | + +--------+--------------------------------------------------------------------+ + | **4** | event protocol for plugin | + +--------+--------------------------------------------------------------------+ + | **5** | any other input configuration (e.g. event name filter, see below) | + +--------+--------------------------------------------------------------------+ + | **6** | second input called ``SecondConsumer`` | + +--------+--------------------------------------------------------------------+ + | **7** | carrier technology for plugin | + +--------+--------------------------------------------------------------------+ + | **8** | event protocol for plugin | + +--------+--------------------------------------------------------------------+ + | **9** | any other plugin configuration | + +--------+--------------------------------------------------------------------+ + | **10** | output interface configuration, APEX output plugins | + +--------+--------------------------------------------------------------------+ + | **11** | first output called ``FirstProducer`` | + +--------+--------------------------------------------------------------------+ + | **12** | carrier technology for plugin | + +--------+--------------------------------------------------------------------+ + | **13** | event protocol for plugin | + +--------+--------------------------------------------------------------------+ + | **14** | any other plugin configuration | + +--------+--------------------------------------------------------------------+ + | **15** | second output called ``SecondProducer`` | + +--------+--------------------------------------------------------------------+ + | **16** | carrier technology for plugin | + +--------+--------------------------------------------------------------------+ + | **17** | event protocol for plugin | + +--------+--------------------------------------------------------------------+ + | **18** | any other output configuration (e.g. event name filter, see below) | + +--------+--------------------------------------------------------------------+ + +Event Filters +############# + + .. container:: paragraph + + APEX will always send an event after a policy execution + is finished. For a successful execution, the event sent + is the output event created by the policy. In case the + policy does not create an output event, APEX will create + a new event with all input event fields plus an + additional field ``exceptionMessage`` with an exception + message. + + .. container:: paragraph + + There are situations in which this auto-generated error + event might not be required or wanted: + + .. container:: ulist + + - when a policy failing should not result in an event + send out via an output interface + + - when the auto-generated event goes back in an APEX + engine (or the same APEX engine), this can create + endless loops + + - the auto-generated event should go to a special output + interface or channel + + .. container:: paragraph + + All of these situations are supported by a filter option + using a wildecard (regular expression) configuration on + APEX I/O interfaces. The parameter is called + ``eventNameFilter`` and the value are `Java regular + expressions `__ + (a + `tutorial `__). + The following code shows some examples: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "eventInputParameters": { + "Input1": { + "carrierTechnologyParameters" : {...}, + "eventProtocolParameters":{...}, + "eventNameFilter" : "^E[Vv][Ee][Nn][Tt][0-9]004$" (1) + } + }, + "eventOutputParameters": { + "Output1": { + "carrierTechnologyParameters":{...}, + "eventProtocolParameters":{...}, + "eventNameFilter" : "^E[Vv][Ee][Nn][Tt][0-9]104$" (2) + } + } + +Executors +--------- + + .. container:: paragraph + + Executors are plugins that realize the execution of logic + contained in a policy model. Logic can be in a task + selector, a task, and a state finalizer. Using plugins for + execution environments makes APEX very flexible to support + virtually any executable logic expressions. + + .. container:: paragraph + + APEX 2.0.0-SNAPSHOT supports the following executors: + + .. container:: ulist + + - Java, for Java implemented logic + + .. container:: ulist + + - This executor requires logic implemented using the + APEX Java interfaces. + + - Generated JAR files must be in the classpath of the + APEX engine at start time. + + - Javascript + + - JRuby, + + - Jython, + + - MVEL + + .. container:: ulist + + - This executor uses the latest version of the MVEL + engine, which can be very hard to debug and can + produce unwanted side effects during execution + +Configure the Javascript Executor +################################# + + .. container:: paragraph + + The Javascript executor is added to the configuration as + follows: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "engineServiceParameters":{ + "engineParameters":{ + "executorParameters":{ + "JAVASCRIPT":{ + "parameterClassName" : + "org.onap.policy.apex.plugins.executor.javascript.JavascriptExecutorParameters" + } + } + } + } + +Configure the Jython Executor +############################# + + .. container:: paragraph + + The Jython executor is added to the configuration as + follows: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "engineServiceParameters":{ + "engineParameters":{ + "executorParameters":{ + "JYTHON":{ + "parameterClassName" : + "org.onap.policy.apex.plugins.executor.jython.JythonExecutorParameters" + } + } + } + } + +Configure the JRuby Executor +############################ + + .. container:: paragraph + + The JRuby executor is added to the configuration as + follows: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "engineServiceParameters":{ + "engineParameters":{ + "executorParameters":{ + "JRUBY":{ + "parameterClassName" : + "org.onap.policy.apex.plugins.executor.jruby.JrubyExecutorParameters" + } + } + } + } + +Configure the Java Executor +########################### + + .. container:: paragraph + + The Java executor is added to the configuration as + follows: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "engineServiceParameters":{ + "engineParameters":{ + "executorParameters":{ + "JAVA":{ + "parameterClassName" : + "org.onap.policy.apex.plugins.executor.java.JavaExecutorParameters" + } + } + } + } + +Configure the MVEL Executor +########################### + + .. container:: paragraph + + The MVEL executor is added to the configuration as + follows: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "engineServiceParameters":{ + "engineParameters":{ + "executorParameters":{ + "MVEL":{ + "parameterClassName" : + "org.onap.policy.apex.plugins.executor.mvel.MVELExecutorParameters" + } + } + } + } + +Context Handlers +---------------- + + .. container:: paragraph + + Context handlers are responsible for all context processing. + There are the following main areas: + + .. container:: ulist + + - Context schema: use schema handlers other than Java class + (supported by default without configuration) + + - Context distribution: distribute context across multiple + APEX engines + + - Context locking: mechanisms to lock context elements for + read/write + + - Context persistence: mechanisms to persist context + + .. container:: paragraph + + APEX provides plugins for each of the main areas. + +Configure AVRO Schema Handler +############################# + + .. container:: paragraph + + The AVRO schema handler is added to the configuration as + follows: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "engineServiceParameters":{ + "engineParameters":{ + "contextParameters":{ + "parameterClassName" : "org.onap.policy.apex.context.parameters.ContextParameters", + "schemaParameters":{ + "Avro":{ + "parameterClassName" : + "org.onap.policy.apex.plugins.context.schema.avro.AvroSchemaHelperParameters" + } + } + } + } + } + + .. container:: paragraph + + Using the AVRO schema handler has one limitation: AVRO + only supports field names that represent valid Java class + names. This means only letters and the character ``_`` + are supported. Characters commonly used in field names, + such as ``.`` and ``-``, are not supported by AVRO. for + more information see `Avro Spec: + Names `__. + + .. container:: paragraph + + To work with this limitation, the APEX Avro plugin will + parse a given AVRO definition and replace *all* + occurrences of ``.`` and ``-`` with a ``_``. This means + that + + .. container:: ulist + + - In a policy model, if the AVRO schema defined a field + as ``my-name`` the policy logic should access it as + ``my_name`` + + - In a policy model, if the AVRO schema defined a field + as ``my.name`` the policy logic should access it as + ``my_name`` + + - There should be no field names that convert to the + same internal name + + .. container:: ulist + + - For instance the simultaneous use of + ``my_name``, ``my.name``, and ``my-name`` should + be avoided + + - If not avoided, the event processing might + create unwanted side effects + + - If field names use any other not-supported character, + the AVRO plugin will reject it + + .. container:: ulist + + - Since AVRO uses lazy initialization, this + rejection might only become visible at runtime + +Carrier Technologies +-------------------- + + .. container:: paragraph + + Carrier technologies define how APEX receives (input) and + sends (output) events. They can be used in any combination, + using asynchronous or synchronous mode. There can also be + any number of carrier technologies for the input (consume) + and the output (produce) interface. + + .. container:: paragraph + + Supported *input* technologies are: + + .. container:: ulist + + - Standard input, read events from the standard input + (console), not suitable for APEX background servers + + - File input, read events from a file + + - Kafka, read events from a Kafka system + + - Websockets, read events from a Websocket + + - JMS, + + - REST (synchronous and asynchronous), additionally as + client or server + + - Event Requestor, allows reading of events that have been + looped back into APEX + + .. container:: paragraph + + Supported *output* technologies are: + + .. container:: ulist + + - Standard output, write events to the standard output + (console), not suitable for APEX background servers + + - File output, write events to a file + + - Kafka, write events to a Kafka system + + - Websockets, write events to a Websocket + + - JMS + + - REST (synchronous and asynchronous), additionally as + client or server + + - Event Requestor, allows events to be looped back into + APEX + + .. container:: paragraph + + New carrier technologies can be added as plugins to APEX or + developed outside APEX and added to an APEX deployment. + +Standard IO +########### + + .. container:: paragraph + + Standard IO does not require a specific plugin, it is + supported be default. + +Standard Input +============== + .. container:: paragraph + + APEX will take events from its standard input. This + carrier is good for testing, but certainly not for a + use case where APEX runs as a server. The + configuration is as follows: + + .. container:: listingblock + + .. container:: content + + :: + + "carrierTechnologyParameters" : { + "carrierTechnology" : "FILE", (1) + "parameters" : { + "standardIO" : true (2) + } + } + + .. container:: colist arabic + + +-------+---------------------------------------+ + | **1** | standard input is considered a file | + +-------+---------------------------------------+ + | **2** | file descriptor set to standard input | + +-------+---------------------------------------+ + +Standard Output +=============== + + .. container:: paragraph + + APEX will send events to its standard output. This + carrier is good for testing, but certainly not for a + use case where APEX runs as a server. The + configuration is as follows: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters" : { + "carrierTechnology" : "FILE", (1) + "parameters" : { + "standardIO" : true (2) + } + } + + .. container:: colist arabic + + +-------+----------------------------------------+ + | **1** | standard output is considered a file | + +-------+----------------------------------------+ + | **2** | file descriptor set to standard output | + +-------+----------------------------------------+ + +2.7.2. File IO +############## + + .. container:: paragraph + + File IO does not require a specific plugin, it is + supported be default. + +File Input +========== + + .. container:: paragraph + + APEX will take events from a file. The same file + should not be used as an output. The configuration is + as follows: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters" : { + "carrierTechnology" : "FILE", (1) + "parameters" : { + "fileName" : "examples/events/SampleDomain/EventsIn.xmlfile" (2) + } + } + + .. container:: colist arabic + + +-------+------------------------------------------+ + | **1** | set file input | + +-------+------------------------------------------+ + | **2** | the name of the file to read events from | + +-------+------------------------------------------+ + +File Output +=========== + .. container:: paragraph + + APEX will write events to a file. The same file should + not be used as an input. The configuration is as + follows: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters" : { + "carrierTechnology" : "FILE", (1) + "parameters" : { + "fileName" : "examples/events/SampleDomain/EventsOut.xmlfile" (2) + } + } + + .. container:: colist arabic + + +-------+-----------------------------------------+ + | **1** | set file output | + +-------+-----------------------------------------+ + | **2** | the name of the file to write events to | + +-------+-----------------------------------------+ + +Event Requestor IO +################## + + .. container:: paragraph + + Event Requestor IO does not require a specific plugin, it + is supported be default. It should only be used with the + APEX event protocol. + +Event Requestor Input +===================== + + .. container:: paragraph + + APEX will take events from APEX. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters" : { + "carrierTechnology": "EVENT_REQUESTOR" (1) + } + + .. container:: colist arabic + + +-------+---------------------------+ + | **1** | set event requestor input | + +-------+---------------------------+ + +Event Requestor Output +====================== + + .. container:: paragraph + + APEX will write events to APEX. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters" : { + "carrierTechnology": "EVENT_REQUESTOR" (1) + } + +Peering Event Requestors +======================== + + .. container:: paragraph + + When using event requestors, they need to be peered. + This means an event requestor output needs to be + peered (associated) with an event requestor input. The + following example shows the use of an event requestor + with the APEX event protocol and the peering of output + and input. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "eventInputParameters": { + "EventRequestorConsumer": { + "carrierTechnologyParameters": { + "carrierTechnology": "EVENT_REQUESTOR" (1) + }, + "eventProtocolParameters": { + "eventProtocol": "APEX" (2) + }, + "eventNameFilter": "InputEvent", (3) + "requestorMode": true, (4) + "requestorPeer": "EventRequestorProducer", (5) + "requestorTimeout": 500 (6) + } + }, + "eventOutputParameters": { + "EventRequestorProducer": { + "carrierTechnologyParameters": { + "carrierTechnology": "EVENT_REQUESTOR" (7) + }, + "eventProtocolParameters": { + "eventProtocol": "APEX" (8) + }, + "eventNameFilter": "EventListEvent", (9) + "requestorMode": true, (10) + "requestorPeer": "EventRequestorConsumer", (11) + "requestorTimeout": 500 (12) + } + } + + .. container:: colist arabic + + +-----------------------------------+-----------------------------------+ + | **1** | event requestor on a consumer | + +-----------------------------------+-----------------------------------+ + | **2** | with APEX event protocol | + +-----------------------------------+-----------------------------------+ + | **3** | optional filter (best to use a | + | | filter to prevent unwanted events | + | | on the consumer side) | + +-----------------------------------+-----------------------------------+ + | **4** | activate requestor mode | + +-----------------------------------+-----------------------------------+ + | **5** | the peer to the output (must | + | | match the output carrier) | + +-----------------------------------+-----------------------------------+ + | **6** | an optional timeout in | + | | milliseconds | + +-----------------------------------+-----------------------------------+ + | **7** | event requestor on a producer | + +-----------------------------------+-----------------------------------+ + | **8** | with APEX event protocol | + +-----------------------------------+-----------------------------------+ + | **9** | optional filter (best to use a | + | | filter to prevent unwanted events | + | | on the consumer side) | + +-----------------------------------+-----------------------------------+ + | **10** | activate requestor mode | + +-----------------------------------+-----------------------------------+ + | **11** | the peer to the output (must | + | | match the input carrier) | + +-----------------------------------+-----------------------------------+ + | **12** | an optional timeout in | + | | milliseconds | + +-----------------------------------+-----------------------------------+ + +Kafka IO +######## + + .. container:: paragraph + + Kafka IO is supported by the APEX Kafka plugin. The + configurations below are examples. APEX will take any + configuration inside the parameter object and forward it + to Kafka. More information on Kafka specific + configuration parameters can be found in the Kafka + documentation: + + .. container:: ulist + + - `Kafka Consumer + Class `__ + + - `Kafka Producer + Class `__ + +Kafka Input +=========== + .. container:: paragraph + + APEX will receive events from the Apache Kafka + messaging system. The input is uni-directional, an + engine will only receive events from the input but not + send any event to the input. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters" : { + "carrierTechnology" : "KAFKA", (1) + "parameterClassName" : + "org.onap.policy.apex.plugins.event.carrier.kafka.KAFKACarrierTechnologyParameters", + "parameters" : { + "bootstrapServers" : "localhost:49092", (2) + "groupId" : "apex-group-id", (3) + "enableAutoCommit" : true, (4) + "autoCommitTime" : 1000, (5) + "sessionTimeout" : 30000, (6) + "consumerPollTime" : 100, (7) + "consumerTopicList" : ["apex-in-0", "apex-in-1"], (8) + "keyDeserializer" : + "org.apache.kafka.common.serialization.StringDeserializer", (9) + "valueDeserializer" : + "org.apache.kafka.common.serialization.StringDeserializer" (10) + } + } + + .. container:: colist arabic + + +--------+-------------------------------------+ + | **1** | set Kafka as carrier technology | + +--------+-------------------------------------+ + | **2** | bootstrap server and port | + +--------+-------------------------------------+ + | **3** | a group identifier | + +--------+-------------------------------------+ + | **4** | flag for auto-commit | + +--------+-------------------------------------+ + | **5** | auto-commit timeout in milliseconds | + +--------+-------------------------------------+ + | **6** | session timeout in milliseconds | + +--------+-------------------------------------+ + | **7** | consumer poll time in milliseconds | + +--------+-------------------------------------+ + | **8** | consumer topic list | + +--------+-------------------------------------+ + | **9** | key for the Kafka de-serializer | + +--------+-------------------------------------+ + | **10** | value for the Kafka de-serializer | + +--------+-------------------------------------+ + +Kafka Output +============ + .. container:: paragraph + + APEX will send events to the Apache Kafka messaging + system. The output is uni-directional, an engine will + send events to the output but not receive any event + from the output. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters" : { + "carrierTechnology" : "KAFKA", (1) + "parameterClassName" : + "org.onap.policy.apex.plugins.event.carrier.kafka.KAFKACarrierTechnologyParameters", + "parameters" : { + "bootstrapServers" : "localhost:49092", (2) + "acks" : "all", (3) + "retries" : 0, (4) + "batchSize" : 16384, (5) + "lingerTime" : 1, (6) + "bufferMemory" : 33554432, (7) + "producerTopic" : "apex-out", (8) + "keySerializer" : + "org.apache.kafka.common.serialization.StringSerializer", (9) + "valueSerializer" : + "org.apache.kafka.common.serialization.StringSerializer" (10) + } + } + + .. container:: colist arabic + + +--------+---------------------------------+ + | **1** | set Kafka as carrier technology | + +--------+---------------------------------+ + | **2** | bootstrap server and port | + +--------+---------------------------------+ + | **3** | acknowledgement strategy | + +--------+---------------------------------+ + | **4** | number of retries | + +--------+---------------------------------+ + | **5** | batch size | + +--------+---------------------------------+ + | **6** | time to linger in milliseconds | + +--------+---------------------------------+ + | **7** | buffer memory in byte | + +--------+---------------------------------+ + | **8** | producer topic | + +--------+---------------------------------+ + | **9** | key for the Kafka serializer | + +--------+---------------------------------+ + | **10** | value for the Kafka serializer | + +--------+---------------------------------+ + +JMS IO +####### + + .. container:: paragraph + + APEX supports the Java Messaging Service (JMS) as input + as well as output. JMS IO is supported by the APEX JMS + plugin. Input and output support an event encoding as + text (JSON string) or object (serialized object). The + input configuration is the same for both encodings, the + output configuration differs. + +JMS Input +========= + .. container:: paragraph + + APEX will receive events from a JMS messaging system. + The input is uni-directional, an engine will only + receive events from the input but not send any event + to the input. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters" : { + "carrierTechnology" : "JMS", (1) + "parameterClassName" : + "org.onap.policy.apex.plugins.event.carrier.jms.JMSCarrierTechnologyParameters", + "parameters" : { (2) + "initialContextFactory" : + "org.jboss.naming.remote.client.InitialContextFactory", (3) + "connectionFactory" : "ConnectionFactory", (4) + "providerURL" : "remote://localhost:5445", (5) + "securityPrincipal" : "guest", (6) + "securityCredentials" : "IAmAGuest", (7) + "consumerTopic" : "jms/topic/apexIn" (8) + } + } + + .. container:: colist arabic + + +-----------------------------------+-----------------------------------+ + | **1** | set JMS as carrier technology | + +-----------------------------------+-----------------------------------+ + | **2** | set all JMS specific parameters | + +-----------------------------------+-----------------------------------+ + | **3** | the context factory, in this case | + | | from JBOSS (it requires the | + | | dependency | + | | org.jboss:jboss-remote-naming:2.0 | + | | .4.Final | + | | or a different version to be in | + | | the directory ``$APEX_HOME/lib`` | + | | or ``%APEX_HOME%\lib`` | + +-----------------------------------+-----------------------------------+ + | **4** | a connection factory for the JMS | + | | connection | + +-----------------------------------+-----------------------------------+ + | **5** | URL with host and port of the JMS | + | | provider | + +-----------------------------------+-----------------------------------+ + | **6** | access credentials, user name | + +-----------------------------------+-----------------------------------+ + | **7** | access credentials, user password | + +-----------------------------------+-----------------------------------+ + | **8** | the JMS topic to listen to | + +-----------------------------------+-----------------------------------+ + +JMS Output with Text +==================== + + .. container:: paragraph + + APEX engine send events to a JMS messaging system. The + output is uni-directional, an engine will send events + to the output but not receive any event from output. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters" : { + "carrierTechnology" : "JMS", (1) + "parameterClassName" : + "org.onap.policy.apex.plugins.event.carrier.jms.JMSCarrierTechnologyParameters", + "parameters" : { (2) + "initialContextFactory" : + "org.jboss.naming.remote.client.InitialContextFactory", (3) + "connectionFactory" : "ConnectionFactory", (4) + "providerURL" : "remote://localhost:5445", (5) + "securityPrincipal" : "guest", (6) + "securityCredentials" : "IAmAGuest", (7) + "producerTopic" : "jms/topic/apexOut", (8) + "objectMessageSending": "false" (9) + } + } + + .. container:: colist arabic + + +-----------------------------------+-----------------------------------+ + | **1** | set JMS as carrier technology | + +-----------------------------------+-----------------------------------+ + | **2** | set all JMS specific parameters | + +-----------------------------------+-----------------------------------+ + | **3** | the context factory, in this case | + | | from JBOSS (it requires the | + | | dependency | + | | org.jboss:jboss-remote-naming:2.0 | + | | .4.Final | + | | or a different version to be in | + | | the directory ``$APEX_HOME/lib`` | + | | or ``%APEX_HOME%\lib`` | + +-----------------------------------+-----------------------------------+ + | **4** | a connection factory for the JMS | + | | connection | + +-----------------------------------+-----------------------------------+ + | **5** | URL with host and port of the JMS | + | | provider | + +-----------------------------------+-----------------------------------+ + | **6** | access credentials, user name | + +-----------------------------------+-----------------------------------+ + | **7** | access credentials, user password | + +-----------------------------------+-----------------------------------+ + | **8** | the JMS topic to write to | + +-----------------------------------+-----------------------------------+ + | **9** | set object messaging to ``false`` | + | | means it sends JSON text | + +-----------------------------------+-----------------------------------+ + +JMS Output with Object +====================== + + .. container:: paragraph + + To configure APEX for JMS objects on the output + interface use the same configuration as above (for + output). Simply change the ``objectMessageSending`` + parameter to ``true``. + +Websocket (WS) IO +######################## + + .. container:: paragraph + + APEX supports the Websockets as input as well as output. + WS IO is supported by the APEX Websocket plugin. This + carrier technology does only support uni-directional + communication. APEX will not send events to a Websocket + input and any event sent to a Websocket output will + result in an error log. + + .. container:: paragraph + + The input can be configured as client (APEX connects to + an existing Websocket server) or server (APEX starts a + Websocket server). The same applies to the output. Input + and output can both use a client or a server + configuration, or separate configurations (input as + client and output as server, input as server and output + as client). Each configuration should use its own + dedicated port to avoid any communication loops. The + configuration of a Websocket client is the same for input + and output. The configuration of a Websocket server is + the same for input and output. + +Websocket Client +================ + + .. container:: paragraph + + APEX will connect to a given Websocket server. As + input, it will receive events from the server but not + send any events. As output, it will send events to the + server and any event received from the server will + result in an error log. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters" : { + "carrierTechnology" : "WEBSOCKET", (1) + "parameterClassName" : + "org.onap.policy.apex.plugins.event.carrier.websocket.WEBSOCKETCarrierTechnologyParameters", + "parameters" : { + "host" : "localhost", (2) + "port" : 42451 (3) + } + } + + .. container:: colist arabic + + +-------+------------------------------------------------------+ + | **1** | set Websocket as carrier technology | + +-------+------------------------------------------------------+ + | **2** | the host name on which a Websocket server is running | + +-------+------------------------------------------------------+ + | **3** | the port of that Websocket server | + +-------+------------------------------------------------------+ + +Websocket Server +================ + + .. container:: paragraph + + APEX will start a Websocket server, which will accept + any Websocket clients to connect. As input, it will + receive events from the server but not send any + events. As output, it will send events to the server + and any event received from the server will result in + an error log. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters" : { + "carrierTechnology" : "WEBSOCKET", (1) + "parameterClassName" : + "org.onap.policy.apex.plugins.event.carrier.websocket.WEBSOCKETCarrierTechnologyParameters", + "parameters" : { + "wsClient" : false, (2) + "port" : 42450 (3) + } + } + + .. container:: colist arabic + + +-------+------------------------------------------------------------+ + | **1** | set Websocket as carrier technology | + +-------+------------------------------------------------------------+ + | **2** | disable client, so that APEX will start a Websocket server | + +-------+------------------------------------------------------------+ + | **3** | the port for the Websocket server APEX will start | + +-------+------------------------------------------------------------+ + +REST Client IO +############## + + .. container:: paragraph + + APEX can act as REST client on the input as well as on + the output interface. The media type is + ``application/json``, so this plugin does only work with + the JSON Event protocol. + +REST Client Input +================= + + .. container:: paragraph + + APEX will connect to a given URL to receive events, + but not send any events. The server is polled, i.e. + APEX will do an HTTP GET, take the result, and then do + the next GET. Any required timing needs to be handled + by the server configured via the URL. For instance, + the server could support a wait timeout via the URL as + ``?timeout=100ms``. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters" : { + "carrierTechnology" : "RESTCLIENT", (1) + "parameterClassName" : + "org.onap.policy.apex.plugins.event.carrier.restclient.RESTClientCarrierTechnologyParameters", + "parameters" : { + "url" : "http://example.org:8080/triggers/events", (2) + } + } + + .. container:: colist arabic + + +-------+---------------------------------------+ + | **1** | set REST client as carrier technology | + +-------+---------------------------------------+ + | **2** | the URL of the HTTP server for events | + +-------+---------------------------------------+ + +REST Client Output +================== + + .. container:: paragraph + + APEX will connect to a given URL to send events, but + not receive any events. The default HTTP operation is + POST (no configuration required). To change it to PUT + simply add the configuration parameter (as shown in + the example below). + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters" : { + "carrierTechnology" : "RESTCLIENT", (1) + "parameterClassName" : + "org.onap.policy.apex.plugins.event.carrier.restclient.RESTClientCarrierTechnologyParameters", + "parameters" : { + "url" : "http://example.com:8888/actions/events", (2) + "httpMethod" : "PUT" (3) + } + } + + .. container:: colist arabic + + +-------+--------------------------------------------------+ + | **1** | set REST client as carrier technology | + +-------+--------------------------------------------------+ + | **2** | the URL of the HTTP server for events | + +-------+--------------------------------------------------+ + | **3** | use HTTP PUT (remove this line to use HTTP POST) | + +-------+--------------------------------------------------+ + +REST Server IO +############## + + .. container:: paragraph + + APEX supports a REST server for input and output. + + .. container:: paragraph + + The REST server plugin always uses a synchronous mode. A + client does a HTTP GET on the APEX REST server with the + input event and receives the generated output event in + the server reply. This means that for the REST server + there has to always to be an input with an associated + output. Input or output only are not permitted. + + .. container:: paragraph + + The plugin will start a Grizzly server as REST server for + a normal APEX engine. If the APEX engine is executed as a + servlet, for instance inside Tomcat, then Tomcat will be + used as REST server (this case requires configuration on + Tomcat as well). + + .. container:: paragraph + + Some configuration restrictions apply for all scenarios: + + .. container:: ulist + + - Minimum port: 1024 + + - Maximum port: 65535 + + - The media type is ``application/json``, so this plugin + does only work with the JSON Event protocol. + + .. container:: paragraph + + The URL the client calls is created using + + .. container:: ulist + + - the configured host and port, e.g. + ``http://localhost:12345`` + + - the standard path, e.g. ``/apex/`` + + - the name of the input/output, e.g. ``FirstConsumer/`` + + - the input or output name, e.g. ``EventIn``. + + .. container:: paragraph + + The examples above lead to the URL + ``http://localhost:12345/apex/FirstConsumer/EventIn``. + + .. container:: paragraph + + A client can also get status information of the REST + server using ``/Status``, e.g. + ``http://localhost:12345/apex/FirstConsumer/Status``. + +REST Server Stand-alone +======================= + + .. container:: paragraph + + We need to configure a REST server input and a REST + server output. Input and output are associated with + each other via there name. + + .. container:: paragraph + + Timeouts for REST calls need to be set carefully. If + they are too short, the call might timeout before a + policy finished creating an event. + + .. container:: paragraph + + The following example configures the input named as + ``MyConsumer`` and associates an output named + ``MyProducer`` with it. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "eventInputParameters": { + "MyConsumer": { + "carrierTechnologyParameters" : { + "carrierTechnology" : "RESTSERVER", (1) + "parameterClassName" : + "org.onap.policy.apex.plugins.event.carrier.restserver.RESTServerCarrierTechnologyParameters", + "parameters" : { + "standalone" : true, (2) + "host" : "localhost", (3) + "port" : 12345 (4) + } + }, + "eventProtocolParameters":{ + "eventProtocol" : "JSON" (5) + }, + "synchronousMode" : true, (6) + "synchronousPeer" : "MyProducer", (7) + "synchronousTimeout" : 500 (8) + } + } + + .. container:: colist arabic + + +-------+---------------------------------------+ + | **1** | set REST server as carrier technology | + +-------+---------------------------------------+ + | **2** | set the server as stand-alone | + +-------+---------------------------------------+ + | **3** | set the server host | + +-------+---------------------------------------+ + | **4** | set the server listen port | + +-------+---------------------------------------+ + | **5** | use JSON event protocol | + +-------+---------------------------------------+ + | **6** | activate synchronous mode | + +-------+---------------------------------------+ + | **7** | associate an output ``MyProducer`` | + +-------+---------------------------------------+ + | **8** | set a timeout of 500 milliseconds | + +-------+---------------------------------------+ + + .. container:: paragraph + + The following example configures the output named as + ``MyProducer`` and associates the input ``MyConsumer`` + with it. Note that for the output there are no more + paramters (such as host or port), since they are + already configured in the associated input + + .. container:: listingblock + + .. container:: content + + .. code:: + + "eventOutputParameters": { + "MyProducer": { + "carrierTechnologyParameters":{ + "carrierTechnology" : "RESTSERVER", + "parameterClassName" : + "org.onap.policy.apex.plugins.event.carrier.restserver.RESTServerCarrierTechnologyParameters" + }, + "eventProtocolParameters":{ + "eventProtocol" : "JSON" + }, + "synchronousMode" : true, + "synchronousPeer" : "MyConsumer", + "synchronousTimeout" : 500 + } + } + +REST Server Stand-alone, multi input +==================================== + + .. container:: paragraph + + Any number of input/output pairs for REST servers can + be configured. For instance, we can configure an input + ``FirstConsumer`` with output ``FirstProducer`` and an + input ``SecondConsumer`` with output + ``SecondProducer``. Important is that there is always + one pair of input/output. + +REST Server Stand-alone in Servlet +================================== + + .. container:: paragraph + + If APEX is executed as a servlet, e.g. inside Tomcat, + the configuration becomes easier since the plugin can + now use Tomcat as the REST server. In this scenario, + there are not parameters (port, host, etc.) and the + key ``standalone`` must not be used (or set to false). + + .. container:: paragraph + + For the Tomcat configuration, we need to add the REST + server plugin, e.g. + + .. container:: listingblock + + .. container:: content + + .. code:: + + + ... + + ... + org.onap.policy.apex.plugins.event.carrier.restserver + + ... + + +REST Requestor IO +################## + + .. container:: paragraph + + APEX can act as REST requestor on the input as well as on + the output interface. The media type is + ``application/json``, so this plugin does only work with + the JSON Event protocol. + +REST Requestor Input +==================== + + .. container:: paragraph + + APEX will connect to a given URL to request an input. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters": { + "carrierTechnology": "RESTREQUESTOR", (1) + "parameterClassName": "org.onap.policy.apex.plugins.event.carrier.restrequestor.RESTRequestorCarrierTechnologyParameters", + "parameters": { + "url": "http://localhost:54321/some/path/to/rest/resource", (2) + "httpMethod": "POST", (3) + "restRequestTimeout": 2000 (4) + } + }, + + .. container:: colist arabic + + +-------+--------------------------------------------------+ + | **1** | set REST requestor as carrier technology | + +-------+--------------------------------------------------+ + | **2** | the URL of the HTTP server for events | + +-------+--------------------------------------------------+ + | **3** | use HTTP PUT (remove this line to use HTTP POST) | + +-------+--------------------------------------------------+ + | **4** | request timeout in milliseconds | + +-------+--------------------------------------------------+ + + .. container:: paragraph + + Further settings are required on the consumer to + define the event that is requested, for example: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "eventName": "GuardResponseEvent", (1) + "eventNameFilter": "GuardResponseEvent", (2) + "requestorMode": true, (3) + "requestorPeer": "GuardRequestorProducer", (4) + "requestorTimeout": 500 (5) + + .. container:: colist arabic + + +-------+---------------------------+ + | **1** | the event name | + +-------+---------------------------+ + | **2** | a filter on the event | + +-------+---------------------------+ + | **3** | the mode of the requestor | + +-------+---------------------------+ + | **4** | a peer for the requestor | + +-------+---------------------------+ + | **5** | a general request timeout | + +-------+---------------------------+ + +REST Requestor Output +===================== + + .. container:: paragraph + + APEX will connect to a given URL to send events, but + not receive any events. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "carrierTechnologyParameters": { + "carrierTechnology": "RESTREQUESTOR", (1) + "parameterClassName": "org.onap.policy.apex.plugins.event.carrier.restrequestor.RESTRequestorCarrierTechnologyParameters" + }, + + .. container:: colist arabic + + +-------+------------------------------------------+ + | **1** | set REST requestor as carrier technology | + +-------+------------------------------------------+ + + .. container:: paragraph + + Further settings are required on the consumer to + define the event that is requested, for example: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "eventNameFilter": "GuardRequestEvent", (1) + "requestorMode": true, (2) + "requestorPeer": "GuardRequestorConsumer", (3) + "requestorTimeout": 500 (4) + + .. container:: colist arabic + + +-------+---------------------------+ + | **1** | a filter on the event | + +-------+---------------------------+ + | **2** | the mode of the requestor | + +-------+---------------------------+ + | **3** | a peer for the requestor | + +-------+---------------------------+ + | **4** | a general request timeout | + +-------+---------------------------+ + +Event Protocols, Format and Encoding +------------------------------------ + + .. container:: paragraph + + Event protocols define what event formats APEX can receive + (input) and should send (output). They can be used in any + combination for input and output, unless further restricted + by a carrier technology plugin (for instance for JMS + output). There can only be 1 event protocol per event + plugin. + + .. container:: paragraph + + Supported *input* event protocols are: + + .. container:: ulist + + - JSON, the event as a JSON string + + - APEX, an APEX event + + - JMS object, the event as a JMS object, + + - JMS text, the event as a JMS text, + + - XML, the event as an XML string, + + - YAML, the event as YAML text + + .. container:: paragraph + + Supported *output* event protocols are: + + .. container:: ulist + + - JSON, the event as a JSON string + + - APEX, an APEX event + + - JMS object, the event as a JMS object, + + - JMS text, the event as a JMS text, + + - XML, the event as an XML string, + + - YAML, the event as YAML text + + .. container:: paragraph + + New event protocols can be added as plugins to APEX or + developed outside APEX and added to an APEX deployment. + +JSON Event +########## + + .. container:: paragraph + + The event protocol for JSON encoding does not require a + specific plugin, it is supported by default. Furthermore, + there is no difference in the configuration for the input + and output interface. + + .. container:: paragraph + + For an input, APEX requires a well-formed JSON string. + Well-formed here means according to the definitions of a + policy. Any JSON string that is not defined as a trigger + event (consume) will not be consumed (errors will be + thrown). For output JSON events, APEX will always produce + valid JSON strings according to the definition in the + policy model. + + .. container:: paragraph + + The following JSON shows the configuration. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "eventProtocolParameters":{ + "eventProtocol" : "JSON" + } + + .. container:: paragraph + + For JSON events, there are a few more optional + parameters, which allow to define a mapping for standard + event fields. An APEX event must have the fields + ``name``, ``version``, ``source``, and ``target`` + defined. Sometimes it is not possible to configure a + trigger or actioning system to use those fields. However, + they might be in an event generated outside APEX (or used + outside APEX) just with different names. To configure + APEX to map between the different event names, simply add + the following parameters to a JSON event: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "eventProtocolParameters":{ + "eventProtocol" : "JSON", + "nameAlias" : "policyName", (1) + "versionAlias" : "policyVersion", (2) + "sourceAlias" : "from", (3) + "targetAlias" : "to", (4) + "nameSpaceAlias": "my.name.space" (5) + } + + .. container:: colist arabic + + +-----------------------------------+-----------------------------------+ + | **1** | mapping for the ``name`` field, | + | | here from a field called | + | | ``policyName`` | + +-----------------------------------+-----------------------------------+ + | **2** | mapping for the ``version`` | + | | field, here from a field called | + | | ``policyVersion`` | + +-----------------------------------+-----------------------------------+ + | **3** | mapping for the ``source`` field, | + | | here from a field called ``from`` | + | | (only for an input event) | + +-----------------------------------+-----------------------------------+ + | **4** | mapping for the ``target`` field, | + | | here from a field called ``to`` | + | | (only for an output event) | + +-----------------------------------+-----------------------------------+ + | **5** | mapping for the ``nameSpace`` | + | | field, here from a field called | + | | ``my.name.space`` | + +-----------------------------------+-----------------------------------+ + +APEX Event +########## + .. container:: paragraph + + The event protocol for APEX events does not require a + specific plugin, it is supported by default. Furthermore, + there is no difference in the configuration for the input + and output interface. + + .. container:: paragraph + + For input and output APEX uses APEX events. + + .. container:: paragraph + + The following JSON shows the configuration. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "eventProtocolParameters":{ + "eventProtocol" : "APEX" + } + +JMS Event +######### + + .. container:: paragraph + + The event protocol for JMS is provided by the APEX JMS + plugin. The plugin supports encoding as JSON text or as + object. There is no difference in the configuration for + the input and output interface. + +JMS Text +======== + .. container:: paragraph + + If used as input, APEX will take a JMS message and + extract a JSON string, then proceed as if a JSON event + was received. If used as output, APEX will take the + event produced by a policy, create a JSON string, and + then wrap it into a JMS message. + + .. container:: paragraph + + The configuration for JMS text is as follows: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "eventProtocolParameters":{ + "eventProtocol" : "JMSTEXT", + "parameterClassName" : + "org.onap.policy.apex.plugins.event.protocol.jms.JMSTextEventProtocolParameters" + } + +JMS Object +========== + .. container:: paragraph + + If used as input, APEX will will take a JMS message, + extract a Java Bean from the ``ObjectMessage`` + message, construct an APEX event and put the bean on + the APEX event as a parameter. If used as output, APEX + will take the event produced by a policy, create a + Java Bean and send it as a JMS message. + + .. container:: paragraph + + The configuration for JMS object is as follows: + + .. container:: listingblock + + .. container:: content + + .. code:: + + "eventProtocolParameters":{ + "eventProtocol" : "JMSOBJECT", + "parameterClassName" : + "org.onap.policy.apex.plugins.event.protocol.jms.JMSObjectEventProtocolParameters" + } + +YAML Event +########## + + .. container:: paragraph + + The event protocol for YAML is provided by the APEX YAML + plugin. There is no difference in the configuration for + the input and output interface. + + .. container:: paragraph + + If used as input, APEX will consume events as YAML and + map them to policy trigger events. Not well-formed YAML + and not understood trigger events will be rejected. If + used as output, APEX produce YAML encoded events from the + event a policy produces. Those events will always be + well-formed according to the definition in the policy + model. + + .. container:: paragraph + + The following code shows the configuration. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "eventProtocolParameters":{ + "eventProtocol" : "XML", + "parameterClassName" : + "org.onap.policy.apex.plugins.event.protocol.yaml.YamlEventProtocolParameters" + } + +XML Event +######### + .. container:: paragraph + + The event protocol for XML is provided by the APEX XML + plugin. There is no difference in the configuration for + the input and output interface. + + .. container:: paragraph + + If used as input, APEX will consume events as XML and map + them to policy trigger events. Not well-formed XML and + not understood trigger events will be rejected. If used + as output, APEX produce XML encoded events from the event + a policy produces. Those events will always be + well-formed according to the definition in the policy + model. + + .. container:: paragraph + + The following code shows the configuration. + + .. container:: listingblock + + .. container:: content + + .. code:: + + "eventProtocolParameters":{ + "eventProtocol" : "XML", + "parameterClassName" : + "org.onap.policy.apex.plugins.event.protocol.xml.XMLEventProtocolParameters" + } + +A configuration example +----------------------- + + .. container:: paragraph + + The following example loads all available plug-ins. + + .. container:: paragraph + + Events are consumed from a Websocket, APEX as client. + Consumed event format is JSON. + + .. container:: paragraph + + Events are produced to Kafka. Produced event format is XML. + + .. container:: listingblock + + .. container:: content + + .. code:: + + { + "engineServiceParameters" : { + "name" : "MyApexEngine", + "version" : "0.0.1", + "id" : 45, + "instanceCount" : 4, + "deploymentPort" : 12345, + "policyModelFileName" : "examples/models/some-model.json", + "engineParameters" : { + "executorParameters" : { + "JAVASCRIPT" : { + "parameterClassName" : + "org.onap.policy.apex.plugins.executor.javascript.JavascriptExecutorParameters" + }, + "JYTHON" : { + "parameterClassName" : + "org.onap.policy.apex.plugins.executor.jython.JythonExecutorParameters" + }, + "JRUBY" : { + "parameterClassName" : + "org.onap.policy.apex.plugins.executor.jruby.JrubyExecutorParameters" + }, + "JAVA" : { + "parameterClassName" : + "org.onap.policy.apex.plugins.executor.java.JavaExecutorParameters" + }, + "MVEL" : { + "parameterClassName" : + "org.onap.policy.apex.plugins.executor.mvel.MVELExecutorParameters" + } + }, + "contextParameters" : { + "parameterClassName" : + "org.onap.policy.apex.context.parameters.ContextParameters", + "schemaParameters" : { + "Avro":{ + "parameterClassName" : + "org.onap.policy.apex.plugins.context.schema.avro.AvroSchemaHelperParameters" + } + } + } + } + }, + "producerCarrierTechnologyParameters" : { + "carrierTechnology" : "KAFKA", + "parameterClassName" : + "org.onap.policy.apex.plugins.event.carrier.kafka.KAFKACarrierTechnologyParameters", + "parameters" : { + "bootstrapServers" : "localhost:49092", + "acks" : "all", + "retries" : 0, + "batchSize" : 16384, + "lingerTime" : 1, + "bufferMemory" : 33554432, + "producerTopic" : "apex-out", + "keySerializer" : "org.apache.kafka.common.serialization.StringSerializer", + "valueSerializer" : "org.apache.kafka.common.serialization.StringSerializer" + } + }, + "producerEventProtocolParameters" : { + "eventProtocol" : "XML", + "parameterClassName" : + "org.onap.policy.apex.plugins.event.protocol.xml.XMLEventProtocolParameters" + }, + "consumerCarrierTechnologyParameters" : { + "carrierTechnology" : "WEBSOCKET", + "parameterClassName" : + "org.onap.policy.apex.plugins.event.carrier.websocket.WEBSOCKETCarrierTechnologyParameters", + "parameters" : { + "host" : "localhost", + "port" : 88888 + } + }, + "consumerEventProtocolParameters" : { + "eventProtocol" : "JSON" + } + } + +Engine and Applications of the APEX System +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Introduction to APEX Engine and Applications +-------------------------------------------- + + .. container:: paragraph + + The core of APEX is the APEX Engine, also known as the APEX + Policy Engine or the APEX PDP (since it is in fact a Policy + Decision Point). Beside this engine, an APEX system comes + with a few applications intended to help with policy + authoring, deployment, and execution. + + .. container:: paragraph + + The engine itself and most applications are started from the + command line with command line arguments. This is called a + Command Line Interface (CLI). Some applications require an + installation on a webserver, as for instance the REST + Editor. Those applications can be accessed via a web + browser. + + .. container:: paragraph + + You can also use the available APEX APIs and applications to + develop other applications as required. This includes policy + languages (and associated parsers and compilers / + interpreters), GUIs to access APEX or to define policies, + clients to connect to APEX, etc. + + .. container:: paragraph + + For this documentation, we assume an installation of APEX as + a full system based on a current ONAP release. + +CLI on Unix, Windows, and Cygwin +-------------------------------- + + .. container:: paragraph + + A note on APEX CLI applications: all applications and the + engine itself have been deployed and tested on different + operating systems: Red Hat, Ubuntu, Debian, Mac OSX, + Windows, Cygwin. Each operating system comes with its own + way of configuring and executing Java. The main items here + are: + + .. container:: ulist + + - For UNIX systems (RHL, Ubuntu, Debian, Mac OSX), the + provided bash scripts work as expected with absolute + paths (e.g. + ``/opt/app/policy/apex-pdp/apex-pdp-2.0.0-SNAPSHOT/examples``), + indirect and linked paths (e.g. ``../apex/apex``), and + path substitutions using environment settings (e.g. + ``$APEX_HOME/bin/``) + + - For Windows systems, the provided batch files (``.bat``) + work as expected with with absolute paths (e.g. + ``C:\apex\apex-2.0.0-SNAPSHOT\examples``), and path + substitutions using environment settings (e.g. + ``%APEX_HOME%\bin\``) + + - For Cygwin system we assume a standard Cygwin + installation with standard tools (mainly bash) using a + Windows Java installation. This means that the bash + scripts can be used as in UNIX, however any argument + pointing to files and directories need to use either a + DOS path (e.g. + ``C:\apex\apex-2.0.0-SNAPSHOT\examples\config...``) or + the command ``cygpath`` with a mixed option. The reason + for that is: Cygwin executes Java using UNIX paths but + then runs Java as a DOS/WINDOWS process, which requires + DOS paths for file access. + +The APEX Engine +--------------- + + .. container:: paragraph + + The APEX engine can be started in different ways, depending + your requirements. All scripts are located in the APEX *bin* + directory + + .. container:: paragraph + + On UNIX and Cygwin systems use: + + .. container:: ulist + + - ``apexEngine.sh`` - this script will + + .. container:: ulist + + - Test if ``$APEX_USER`` is set and if the user + exists, terminate with an error otherwise + + - Test if ``$APEX_HOME`` is set. If not set, it will + use the default setting as + ``/opt/app/policy/apex-pdp/apex-pdp``. Then the set + directory is tested to exist, the script will + terminate if not. + + - When all tests are passed successfully, the script + will call ``apexApps.sh`` with arguments to start + the APEX engine. + + - ``apexApps.sh engine`` - this is the general APEX + application launcher, which will + + .. container:: ulist + + - Start the engine with the argument ``engine`` + + - Test if ``$APEX_HOME`` is set and points to an + existing directory. If not set or directory does + not exist, script terminates. + + - Not test for any settings of ``$APEX_USER``. + + .. container:: paragraph + + On Windows systems use ``apexEngine.bat`` and + ``apexApps.bat engine`` respectively. Note: none of the + windows batch files will test for ``%APEX_USER%``. + + .. container:: paragraph + + Summary of alternatives to start the APEX Engine: + + +--------------------------------------------------------+----------------------------------------------------------+ + | Unix, Cygwin | Windows | + +========================================================+==========================================================+ + | .. container:: | .. container:: | + | | | + | .. container:: listingblock | .. container:: listingblock | + | | | + | .. container:: content | .. container:: content | + | | | + | .. code:: | .. code:: | + | | | + | # $APEX_HOME/bin/apexEngine.sh [args] | > %APEX_HOME%\bin\apexEngine.bat [args] | + | # $APEX_HOME/bin/apexApps.sh engine [args] | > %APEX_HOME%\bin\apexApps.bat engine [args] | + +--------------------------------------------------------+----------------------------------------------------------+ + + .. container:: paragraph + + The APEX engine comes with a few CLI arguments for setting + configuration and policy model. The configuration file is + always required. The policy model file is only required if + no model file is specified in the configuration, or if the + specified model file should be over written. The option + ``-h`` prints a help screen. + + .. container:: listingblock + + .. container:: content + + .. code:: + + usage: org.onap.policy.apex.service.engine.main.ApexMain [options...] + options + -c,--config-file the full path to the configuration file to use, the configuration file must be a Json file + containing the Apex configuration parameters + -h,--help outputs the usage of this command + -m,--model-file the full path to the model file to use, if set it overrides the model file set in the + configuration file + -v,--version outputs the version of Apex + +The APEX CLI Editor +------------------- + + .. container:: paragraph + + The CLI Editor allows to define policies from the command + line. The application uses a simple language and supports + all elements of an APEX policy. It can be used in to + different ways: + + .. container:: ulist + + - non-interactive, specifying a file with the commands to + create a policy + + - interactive, using the editors CLI to create a policy + + .. container:: paragraph + + When a policy is fully specified, the editor will generate + the APEX core policy specification in JSON. This core + specification is called the policy model in the APEX engine + and can be used directly with the APEX engine. + + .. container:: paragraph + + On UNIX and Cygwin systems use: + + .. container:: ulist + + - ``apexCLIEditor.sh`` - simply starts the CLI editor, + arguments to the script determine the mode of the editor + + - ``apexApps.sh cli-editor`` - simply starts the CLI + editor, arguments to the script determine the mode of the + editor + + .. container:: paragraph + + On Windows systems use: + + .. container:: ulist + + - ``apexCLIEditor.bat`` - simply starts the CLI editor, + arguments to the script determine the mode of the editor + + - ``apexApps.bat cli-editor`` - simply starts the CLI + editor, arguments to the script determine the mode of the + editor + + .. container:: paragraph + + Summary of alternatives to start the APEX CLI Editor: + + +------------------------------------------------------------+--------------------------------------------------------------+ + | Unix, Cygwin | Windows | + +============================================================+==============================================================+ + | .. container:: | .. container:: | + | | | + | .. container:: listingblock | .. container:: listingblock | + | | | + | .. container:: content | .. container:: content | + | | | + | .. code:: | .. code:: | + | | | + | # $APEX_HOME/bin/apexCLIEditor.sh.sh [args] | > %APEX_HOME%\bin\apexCLIEditor.bat [args] | + | # $APEX_HOME/bin/apexApps.sh cli-editor [args] | > %APEX_HOME%\bin\apexApps.bat cli-editor [args] | + +------------------------------------------------------------+--------------------------------------------------------------+ + + .. container:: paragraph + + The option ``-h`` provides a help screen with all command + line arguments. + + .. container:: listingblock + + .. container:: content + + .. code:: + + usage: org.onap.policy.apex.auth.clieditor.ApexCLIEditorMain [options...] + options + -a,--model-props-file name of the apex model properties file to use + -c,--command-file name of a file containing editor commands to run into the editor + -h,--help outputs the usage of this command + -i,--input-model-file name of a file that contains an input model for the editor + -if,--ignore-failures true or false, ignore failures of commands in command files and continue + executing the command file + -l,--log-file name of a file that will contain command logs from the editor, will log + to standard output if not specified or suppressed with "-nl" flag + -m,--metadata-file name of the command metadata file to use + -nl,--no-log if specified, no logging or output of commands to standard output or log + file is carried out + -nm,--no-model-output if specified, no output of a model to standard output or model output + file is carried out, the user can use the "save" command in a script to + save a model + -o,--output-model-file name of a file that will contain the output model for the editor, will + output model to standard output if not specified or suppressed with + "-nm" flag + -wd,--working-directory the working directory that is the root for the CLI editor and is the + root from which to look for included macro files + +The APEX REST Editor +-------------------- + + .. container:: paragraph + + The standard way to use the APEX REST Editor is via an + installation of the *war* file on a webserver. However, the + REST editor can also be started via command line. This will + start a Grizzly webserver with the *war* deployed. Access to + the REST Editor is then via the provided URL + + .. container:: paragraph + + On UNIX and Cygwin systems use: + + .. container:: ulist + + - ``apexRESTEditor.sh`` - simply starts the webserver with + the REST editor + + - ``apexApps.sh rest-editor`` - simply starts the webserver + with the REST editor + + .. container:: paragraph + + On Windows systems use: + + .. container:: ulist + + - ``apexRESTEditor.bat`` - simply starts the webserver with + the REST editor + + - ``apexApps.bat rest-editor`` - simply starts the + webserver with the REST editor + + .. container:: paragraph + + Summary of alternatives to start the APEX REST Editor: + + +-------------------------------------------------------------+---------------------------------------------------------------+ + | Unix, Cygwin | Windows | + +=============================================================+===============================================================+ + | .. container:: | .. container:: | + | | | + | .. container:: listingblock | .. container:: listingblock | + | | | + | .. container:: content | .. container:: content | + | | | + | .. code:: | .. code:: | + | | | + | # $APEX_HOME/bin/apexRESTEditor.sh.sh [args] | > %APEX_HOME%\bin\apexRESTEditor.bat [args] | + | # $APEX_HOME/bin/apexApps.sh rest-editor [args] | > %APEX_HOME%\bin\apexApps.bat rest-editor [args] | + +-------------------------------------------------------------+---------------------------------------------------------------+ + + .. container:: paragraph + + The option ``-h`` provides a help screen with all command + line arguments. + + .. container:: listingblock + + .. container:: content + + .. code:: + + usage: org.onap.policy.apex.client.editor.rest.ApexEditorMain [options...] + -h,--help outputs the usage of this command + -l,--listen
the IP address to listen on. Default value is localhost to restrict access to the + local machine only. + -p,--port port to use for the Apex RESTful editor REST calls. + -t,--time-to-live the amount of time in seconds that the server will run for before terminating. Default + value is -1 to run indefinitely. + + .. container:: paragraph + + If the REST Editor is started without any arguments the + final messages will look similar to this: + + .. container:: listingblock + + .. container:: content + + .. code:: + + Apex Editor REST endpoint (ApexEditorMain: Config=[ApexEditorParameters: URI=http://localhost:18989/apexservices/, TTL=-1sec], State=READY) starting at http://localhost:18989/apexservices/ . . . + Sep 05, 2018 11:24:30 PM org.glassfish.grizzly.http.server.NetworkListener start + INFO: Started listener bound to [localhost:18989] + Sep 05, 2018 11:24:30 PM org.glassfish.grizzly.http.server.HttpServer start + INFO: [HttpServer] Started. + Apex Editor REST endpoint (ApexEditorMain: Config=[ApexEditorParameters: URI=http://localhost:18989/apexservices/, TTL=-1sec], State=RUNNING) started at http://localhost:18989/apexservices/ + + .. container:: paragraph + + The last line states the URL on which the REST Editor can be + accessed. The example above stated + ``http://0.0.0.0:18989/apex/``. In a web browser use the URL + ``http://localhost:18989`` and the REST Editor will start. + +The APEX Monitoring Client +-------------------------- + + .. container:: paragraph + + The standard way to use the APEX Monitoring Client is via an + installation of the *war* file on a webserver. However, the + Monitoring Client can also be started via command line. This + will start a Grizzly webserver with the *war* deployed. + Access to the Monitoring Client is then via the provided URL + + .. container:: paragraph + + On UNIX and Cygwin systems use: + + .. container:: ulist + + - ``apexApps.sh eng-monitoring`` - simply starts the + webserver with the Monitoring Client + + .. container:: paragraph + + On Windows systems use: + + .. container:: ulist + + - ``apexApps.bat eng-monitoring`` - simply starts the + webserver with the Monitoring Client + + .. container:: paragraph + + The option ``-h`` provides a help screen with all command + line arguments. + + .. container:: listingblock + + .. container:: content + + .. code:: + + usage: org.onap.policy.apex.client.monitoring.rest.ApexMonitoringRestMain [options...] + -h,--help outputs the usage of this command + -p,--port port to use for the Apex Services REST calls + -t,--time-to-live the amount of time in seconds that the server will run for before terminating + + .. container:: paragraph + + If the Monitoring Client is started without any arguments + the final messages will look similar to this: + + .. container:: listingblock + + .. container:: content + + .. code:: + + Apex Services REST endpoint (ApexMonitoringRestMain: Config=[ApexMonitoringRestParameters: URI=http://localhost:18989/apexservices/, TTL=-1sec], State=READY) starting at http://localhost:18989/apexservices/ . . . + Sep 05, 2018 11:26:20 PM org.glassfish.grizzly.http.server.NetworkListener start + INFO: Started listener bound to [localhost:18989] + Sep 05, 2018 11:26:20 PM org.glassfish.grizzly.http.server.HttpServer start + INFO: [HttpServer] Started. + Apex Services REST endpoint (ApexMonitoringRestMain: Config=[ApexMonitoringRestParameters: URI=http://localhost:18989/apexservices/, TTL=-1sec], State=RUNNING) started at http://localhost:18989/apexservices/ + + .. container:: paragraph + + The last line states the URL on which the Monitoring Client + can be accessed. The example above stated + ``http://localhost:18989/apexservices``. In a web browser + use the URL ``http://localhost:18989``. + +The APEX Deployment Client +-------------------------- + + .. container:: paragraph + + The standard way to use the APEX Deployment Client is via an + installation of the *war* file on a webserver. However, the + Deployment Client can also be started via command line. This + will start a Grizzly webserver with the *war* deployed. + Access to the Deployment Client is then via the provided URL + + .. container:: paragraph + + On UNIX and Cygwin systems use: + + .. container:: ulist + + - ``apexApps.sh eng-deployment`` - simply starts the + webserver with the Deployment Client + + .. container:: paragraph + + On Windows systems use: + + .. container:: ulist + + - ``apexApps.bat eng-deployment`` - simply starts the + webserver with the Deployment Client + + .. container:: paragraph + + The option ``-h`` provides a help screen with all command + line arguments. + + .. container:: listingblock + + .. container:: content + + .. code:: + + usage: org.onap.policy.apex.client.deployment.rest.ApexDeploymentRestMain [options...] + -h,--help outputs the usage of this command + -p,--port port to use for the Apex Services REST calls + -t,--time-to-live the amount of time in seconds that the server will run for before terminating + + .. container:: paragraph + + If the Deployment Client is started without any arguments + the final messages will look similar to this: + + .. container:: listingblock + + .. container:: content + + .. code:: + + Apex Services REST endpoint (ApexDeploymentRestMain: Config=[ApexDeploymentRestParameters: URI=http://localhost:18989/apexservices/, TTL=-1sec], State=READY) starting at http://localhost:18989/apexservices/ . . . + Sep 05, 2018 11:27:09 PM org.glassfish.grizzly.http.server.NetworkListener start + INFO: Started listener bound to [localhost:18989] + Sep 05, 2018 11:27:09 PM org.glassfish.grizzly.http.server.HttpServer start + INFO: [HttpServer] Started. + Apex Services REST endpoint (ApexDeploymentRestMain: Config=[ApexDeploymentRestParameters: URI=http://localhost:18989/apexservices/, TTL=-1sec], State=RUNNING) started at http://localhost:18989/apexservices/ + + .. container:: paragraph + + The last line states the URL on which the Deployment Client + can be accessed. The example above stated + ``http://localhost:18989/apexservices``. In a web browser + use the URL ``http://localhost:18989``. + +The APEX Full Client +-------------------- + + .. container:: paragraph + + The APEX Full Client combines the REST Editor, the + Monitoring Client, and the Deployment Client into a single + application. The standard way to use the APEX Full Client is + via an installation of the *war* file on a webserver. + However, the Full Client can also be started via command + line. This will start a Grizzly webserver with the *war* + deployed. Access to the Full Client is then via the provided + URL + + .. container:: paragraph + + On UNIX and Cygwin systems use: + + .. container:: ulist + + - ``apexApps.sh full-client`` - simply starts the webserver + with the Full Client + + .. container:: paragraph + + On Windows systems use: + + .. container:: ulist + + - ``apexApps.bat full-client`` - simply starts the + webserver with the Full Client + + .. container:: paragraph + + The option ``-h`` provides a help screen with all command + line arguments. + + .. container:: listingblock + + .. container:: content + + .. code:: + + usage: org.onap.policy.apex.client.full.rest.ApexServicesRestMain [options...] + -h,--help outputs the usage of this command + -p,--port port to use for the Apex Services REST calls + -t,--time-to-live the amount of time in seconds that the server will run for before terminating + + .. container:: paragraph + + If the Full Client is started without any arguments the + final messages will look similar to this: + + .. container:: listingblock + + .. container:: content + + .. code:: + + Apex Editor REST endpoint (ApexServicesRestMain: Config=[ApexServicesRestParameters: URI=http://localhost:18989/apexservices/, TTL=-1sec], State=READY) starting at http://localhost:18989/apexservices/ . . . + Sep 05, 2018 11:28:28 PM org.glassfish.grizzly.http.server.NetworkListener start + INFO: Started listener bound to [localhost:18989] + Sep 05, 2018 11:28:28 PM org.glassfish.grizzly.http.server.HttpServer start + INFO: [HttpServer] Started. + Apex Editor REST endpoint (ApexServicesRestMain: Config=[ApexServicesRestParameters: URI=http://localhost:18989/apexservices/, TTL=-1sec], State=RUNNING) started at http://localhost:18989/apexservices/ + + .. container:: paragraph + + The last line states the URL on which the Monitoring Client + can be accessed. The example above stated + ``http://localhost:18989/apexservices``. In a web browser + use the URL ``http://localhost:18989``. + + The APEX Application Launcher +------------------------------ + + .. container:: paragraph + + The standard applications (Engine, CLI Editor, REST Editor) + come with dedicated start scripts. For all other APEX + applications, we provide an application launcher. + + .. container:: paragraph + + On UNIX and Cygwin systems use: + + .. container:: ulist + + - apexApps.sh\` - simply starts the application launcher + + .. container:: paragraph + + On Windows systems use: + + .. container:: ulist + + - ``apexApps.bat`` - simply starts the application launcher + + .. container:: paragraph + + Summary of alternatives to start the APEX application + launcher: + + +-------------------------------------------------+---------------------------------------------------+ + | Unix, Cygwin | Windows | + +=================================================+===================================================+ + | .. container:: | .. container:: | + | | | + | .. container:: listingblock | .. container:: listingblock | + | | | + | .. container:: content | .. container:: content | + | | | + | .. code:: | .. code:: | + | | | + | # $APEX_HOME/bin/apexApps.sh [args] | > %APEX_HOME%\bin\apexApps.bat [args] | + +-------------------------------------------------+---------------------------------------------------+ + + .. container:: paragraph + + The option ``-h`` provides a help screen with all launcher + command line arguments. + + .. container:: listingblock + + .. container:: content + + .. code:: + + apexApps.sh - runs APEX applications + + Usage: apexApps.sh [options] | [ []] + + Options + -d - describes an application + -l - lists all applications supported by this script + -h - this help screen + + .. container:: paragraph + + Using ``-l`` lists all known application the launcher can + start. + + .. container:: listingblock + + .. container:: content + + .. code:: + + apexApps.sh: supported applications: + --> ws-echo engine eng-monitoring full-client eng-deployment tpl-event-json model-2-cli rest-editor cli-editor ws-console + + .. container:: paragraph + + Using the ``-d `` option describes the named + application, for instance for the ``ws-console``: + + .. container:: listingblock + + .. container:: content + + .. code:: + + apexApps.sh: application 'ws-console' + --> a simple console sending events to APEX, connect to APEX consumer port + + .. container:: paragraph + + Launching an application is done by calling the script with + only the application name and any CLI arguments for the + application. For instance, starting the ``ws-echo`` + application with port ``8888``: + + .. container:: listingblock + + .. container:: content + + .. code:: + + apexApps.sh ws-echo -p 8888 + +Application: Create Event Templates +----------------------------------- + + .. container:: paragraph + + **Status: Experimental** + + .. container:: paragraph + + This application takes a policy model (JSON or XML encoded) + and generates templates for events in JSON format. This can + help when a policy defines rather complex trigger or action + events or complex events between states. The application can + produce events for the types: stimuli (policy trigger + events), internal (events between policy states), and + response (action events). + + +----------------------------------------------------------------+------------------------------------------------------------------+ + | Unix, Cygwin | Windows | + +================================================================+==================================================================+ + | .. container:: | .. container:: | + | | | + | .. container:: listingblock | .. container:: listingblock | + | | | + | .. container:: content | .. container:: content | + | | | + | .. code:: | .. code:: | + | | | + | # $APEX_HOME/bin/apexApps.sh tpl-event-json [args] | > %APEX_HOME%\bin\apexApps.bat tpl-event-json [args] | + +----------------------------------------------------------------+------------------------------------------------------------------+ + + .. container:: paragraph + + The option ``-h`` provides a help screen. + + .. container:: listingblock + + .. container:: content + + .. code:: + + gen-model2event v{release-version} - generates JSON templates for events generated from a policy model + usage: gen-model2event + -h,--help prints this help and usage screen + -m,--model set the input policy model file + -t,--type set the event type for generation, one of: + stimuli (trigger events), response (action + events), internal (events between states) + -v,--version prints the application version + + .. container:: paragraph + + The created templates are not valid events, instead they use + some markup for values one will need to change to actual + values. For instance, running the tool with the *Sample + Domain* policy model as: + + .. container:: listingblock + + .. container:: content + + .. code:: + + apexApps.sh tpl-event-json -m $APEX_HOME/examples/models/SampleDomain/SamplePolicyModelJAVA.json -t stimuli + + .. container:: paragraph + + will produce the following status messages: + + .. container:: listingblock + + .. container:: content + + .. code:: + + gen-model2event: starting Event generator + --> model file: examples/models/SampleDomain/SamplePolicyModelJAVA.json + --> type: stimuli + + .. container:: paragraph + + and then run the generator application producing two event + templates. The first template is called ``Event0000``. + + .. container:: listingblock + + .. container:: content + + .. code:: + + { + "name" : "Event0000", + "nameSpace" : "org.onap.policy.apex.sample.events", + "version" : "0.0.1", + "source" : "Outside", + "target" : "Match", + "TestTemperature" : ###double: 0.0###, + "TestTimestamp" : ###long: 0###, + "TestMatchCase" : ###integer: 0###, + "TestSlogan" : "###string###" + } + + .. container:: paragraph + + The values for the keys are marked with ``#`` and the + expected type of the value. To create an actual stimuli + event, all these markers need to be change to actual values, + for instance: + + .. container:: listingblock + + .. container:: content + + .. code:: + + { + "name" : "Event0000", + "nameSpace" : "org.onap.policy.apex.sample.events", + "version" : "0.0.1", + "source" : "Outside", + "target" : "Match", + "TestTemperature" : 25, + "TestTimestamp" : 123456789123456789, + "TestMatchCase" : 1, + "TestSlogan" : "Testing the Match Case with Temperature 25" + } + +Application: Convert a Policy Model to CLI Editor Commands +---------------------------------------------------------- + + .. container:: paragraph + + **Status: Experimental** + + .. container:: paragraph + + This application takes a policy model (JSON or XML encoded) + and generates commands for the APEX CLI Editor. This + effectively reverses a policy specification realized with + the CLI Editor. + + +-------------------------------------------------------------+---------------------------------------------------------------+ + | Unix, Cygwin | Windows | + +=============================================================+===============================================================+ + | .. container:: | .. container:: | + | | | + | .. container:: listingblock | .. container:: listingblock | + | | | + | .. container:: content | .. container:: content | + | | | + | .. code:: | .. code:: | + | | | + | # $APEX_HOME/bin/apexApps.sh model-2-cli [args] | > %APEX_HOME%\bin\apexApps.bat model-2-cli [args] | + +-------------------------------------------------------------+---------------------------------------------------------------+ + + .. container:: paragraph + + The option ``-h`` provides a help screen. + + .. container:: listingblock + + .. container:: content + + .. code:: + + usage: gen-model2cli + -h,--help prints this help and usage screen + -m,--model set the input policy model file + -sv,--skip-validation switch of validation of the input file + -v,--version prints the application version + + .. container:: paragraph + + For instance, running the tool with the *Sample Domain* + policy model as: + + .. container:: listingblock + + .. container:: content + + .. code:: + + apexApps.sh model-2-cli -m $APEX_HOME/examples/models/SampleDomain/SamplePolicyModelJAVA.json + + .. container:: paragraph + + will produce the following status messages: + + .. container:: listingblock + + .. container:: content + + .. code:: + + gen-model2cli: starting CLI generator + --> model file: examples/models/SampleDomain/SamplePolicyModelJAVA.json + + .. container:: paragraph + + and then run the generator application producing all CLI + Editor commands and printing them to standard out. + +Application: Websocket Clients (Echo and Console) +------------------------------------------------- + + .. container:: paragraph + + **Status: Production** + + .. container:: paragraph + + The application launcher also provides a Websocket echo + client and a Websocket console client. The echo client + connects to APEX and prints all events it receives from + APEX. The console client connects to APEX, reads input from + the command line, and sends this input as events to APEX. + + +------------------------------------------------------------+--------------------------------------------------------------+ + | Unix, Cygwin | Windows | + +============================================================+==============================================================+ + | .. container:: | .. container:: | + | | | + | .. container:: listingblock | .. container:: listingblock | + | | | + | .. container:: content | .. container:: content | + | | | + | .. code:: | .. code:: | + | | | + | # $APEX_HOME/bin/apexApps.sh ws-echo [args] | > %APEX_HOME%\bin\apexApps.bat ws-echo [args] | + | # $APEX_HOME/bin/apexApps.sh ws-console [args] | > %APEX_HOME%\bin\apexApps.bat ws-console [args] | + +------------------------------------------------------------+--------------------------------------------------------------+ + + .. container:: paragraph + + The arguments are the same for both applications: + + .. container:: ulist + + - ``-p`` defines the Websocket port to connect to (defaults + to ``8887``) + + - ``-s`` defines the host on which a Websocket server is + running (defaults to ``localhost``) + + .. container:: paragraph + + A discussion on how to use these two applications to build + an APEX system is detailed HowTo-Websockets. + +My First Policy +^^^^^^^^^^^^^^^ + +Introduction +------------ + .. container:: paragraph + + Consider a scenario where a supermarket chain called + *HyperM* controls how it sells items in a policy-based + manner. Each time an item is processed by *HyperM*'s + point-of-sale (PoS) system an event is generated and + published about that item of stock being sold. This event + can then be used to update stock levels, etc.. + + .. container:: paragraph + + *HyperM* want to extend this approach to allow some checks + to be performed before the sale can be completed. This can + be achieved by requesting a policy-controlled decision as + each item is processed by for sale by each PoS system. The + decision process is integrated with *HyperM*'s other IT + systems that manage stock control, sourcing and purchasing, + personnel systems, etc. + + .. container:: paragraph + + In this document we will show how APEX and APEX Policies can + be used to achieve this, starting with a simple policy, + building up to more complicated policy that demonstrates the + features of APEX. + +Data Models +----------- + +Sales Input Event +################# + + .. container:: paragraph + + Each time a PoS system processes a sales item an event + with the following format is emitted: + + .. table:: Table 1. Sale Input Event + + +----------------------+----------------------+-----------------------+ + | Event | Fields | Description | + +======================+======================+=======================+ + | SALE_INPUT | time, sale_ID, | Event indicating a | + | | amount, item_ID, | sale of an item is | + | | quantity, | occurring | + | | assistant_ID, | | + | | branch_ID, notes, …​ | | + +----------------------+----------------------+-----------------------+ + + .. container:: paragraph + + In each ``SALE_INPUT`` event the ``sale_ID`` field is a + unique ID generated by the PoS system. A timestamp for + the event is stored in the ``time`` field. The ``amount`` + field refers to the value of the item(s) to be sold (in + cents). The ``item_ID`` field is a unique identifier for + each item type, and can be used to retrieve more + information about the item from *HyperM*'s stock control + system. The ``quantity`` field refers to the quantity of + the item to be sold. The ``assistant_ID`` field is a + unique identifier for the PoS operator, and can be used + to retrieve more information about the operator from the + *HyperM*'s personnel system. Since *HyperM* has many + branches the ``branch_ID`` identifies the shop. The + ``notes`` field contains arbitrary notes about the sale. + +Sales Decision Event +#################### + + .. container:: paragraph + + After a ``SALE_INPUT`` event is emitted by the PoS system + *HyperM*'s policy-based controlled sales checking system + emits a Sale Authorization Event indicating whether the + sale is authorized or denied. The PoS system can then + listen for this event before continuing with the sale. + + .. table:: Table 2. Sale Authorisation Event + + +----------------------+----------------------+-----------------------+ + | Event | Fields | Description | + +======================+======================+=======================+ + | SALE_AUTH | sale_ID, time, | Event indicating a | + | | authorized, amount, | sale of an item is | + | | item_ID, quantity, | authorized or denied | + | | assistant_ID, | | + | | branch_ID, notes, | | + | | message…​ | | + +----------------------+----------------------+-----------------------+ + + .. container:: paragraph + + In each ``SALE_AUTH`` event the ``sale_ID`` field is + copied from the ``SALE_INPUT`` event that trigger the + decision request. The ``SALE_AUTH`` event is also + timestamped using the ``time`` field, and a field called + ``authorised`` is set to ``true`` or ``false`` depending + on whether the sale is authorized or denied. The + ``message`` field carries an optional message about why a + sale was not authorized. The other fields from the + ``SALE_INPUT`` event are also included for completeness. + +Stock Control: Items +#################### + + .. container:: paragraph + + *HyperM* maintains information about each item for sale + in a database table called ``ITEMS``. + + .. table:: Table 3. Items Database + + +----------------------+----------------------+-----------------------+ + | Table | Fields | Description | + +======================+======================+=======================+ + | ITEMS | item_ID, | Database table | + | | description, | describing each item | + | | cost_price, barcode, | for sale | + | | supplier_ID, | | + | | category, …​ | | + +----------------------+----------------------+-----------------------+ + + .. container:: paragraph + + The database table ``ITEMS`` has a row for each items + that *HyperM* sells. Each item is identified by an + ``item_ID`` value. The ``description`` field stores a + description of the item. The cost price of the item is + given in ``cost_price``. The barcode of the item is + encoded in ``barcode``, while the item supplier is + identified by ``supplier_ID``. Items may also be + classified into categories using the ``category`` field. + Useful categories might include: ``soft drinks``, + ``alcoholic drinks``, ``cigarettes``, ``knives``, + ``confectionery``, ``bakery``, ``fruit&vegetables``, + ``meat``, etc.. + +Personnel System: Assistants +############################ + + .. table:: Table 4. Assistants Database + + +----------------------+----------------------+-----------------------+ + | Table | Fields | Description | + +======================+======================+=======================+ + | ASSISTANTS | assistant_ID, | Database table | + | | surname, firstname, | describing each | + | | middlename, age, | *HyperM* sales | + | | grade, phone_number, | assistant | + | | …​ | | + +----------------------+----------------------+-----------------------+ + + .. container:: paragraph + + The database table ``ASSISTANTS`` has a row for each + sales assistant employed by *HyperM*. Each assistant is + identified by an ``assistant_ID`` value, with their name + given in the ``firstname``, ``middlename`` and + ``surname`` fields. The assistant’s age in years is given + in ``age``, while their phone number is contained in the + ``phone_number`` field. The assistant’s grade is encoded + in ``grade``. Useful values for ``grade`` might include: + ``trainee``, ``operator``, ``supervisor``, etc.. + +Locations: Branches +#################### + + .. table:: Table 5. Branches Database + + +----------------------+----------------------+-----------------------+ + | Table | Fields | Description | + +======================+======================+=======================+ + | BRANCHES | branch_ID, | Database table | + | | branch_Name, | describing each | + | | category, street, | *HyperM* branch | + | | city, country, | | + | | postcode, …​ | | + +----------------------+----------------------+-----------------------+ + + .. container:: paragraph + + *HyperM* operates a number of branches. Each branch is + described in the ``BRANCHES`` database table. Each branch + is identified by a ``branch_ID``, with a branch name + given in ``branch_Name``. The address for the branch is + encoded in ``street``, ``city``, ``country`` and + ``postcode``. The branch category is given in the + ``category`` field. Useful values for ``category`` might + include: ``Small``, ``Large``, ``Super``, ``Hyper``, + etc.. + +Policy Step 1 +------------- + +Scenario +######### + .. container:: paragraph + + For the first version of our policy, let’s start with + something simple. Let us assume that there exists some + restriction that alcohol products cannot be sold before + 11:30am. In this section we will go through the necessary + steps to define a policy that can enforce this for + *HyperM*. + + .. container:: ulist + + - Alcohol cannot be sold before 11:30am. + +Create the an new empty Policy Model ``MyFirstPolicyModel`` +########################################################### + + .. container:: paragraph + + Since an organisation like *HyperM* may have many + policies covering many different domains, policies should + be grouped into policy sets. In order to edit or deploy a + policy, or policy set, the definition of the policy(ies) + and all required events, tasks, states, etc., are grouped + together into a 'Policy Model'. An organization might + define many Policy Models, each containing a different + set of policies. + + .. container:: paragraph + + So the first step is to create a new empty Policy Model + called ``MyFirstPolicyModel``. Using the APEX Policy + Editor, click on the 'File' menus and select 'New'. Then + define our new policy model called + ``MyFirstPolicyModel``. Use the 'Generate UUID' button to + create a new unique ID for the policy model, and fill in + a description for the policy model. Press the ``Submit`` + button to save your changes. + + .. container:: imageblock + + .. container:: content + + |File > New to create a new Policy Model| + + .. container:: title + + Figure 4. Create a new Policy Model 1/2 + + .. container:: imageblock + + .. container:: content + + |Create a new Policy Model| + + .. container:: title + + Figure 5. Create a new Policy Model 2/2 + +Create the input event ``SALE_INPUT`` and the output event ``SALE_AUTH`` +######################################################################## + + .. container:: paragraph + + Using the APEX Policy Editor, click on the 'Events' tab. + In the 'Events' pane, right click and select 'New': + + .. container:: imageblock + + .. container:: content + + |Right click to create a new event| + + .. container:: title + + Figure 6. Create a new Event type + + .. container:: paragraph + + Create a new event type called ``SALE_INPUT``. Use the + 'Generate UUID' button to create a new unique ID for the + event type, and fill in a description for the event. Add + a namespace, e.g. ``com.hyperm``. We can add hard-coded + strings for the ``Source`` and ``Target``, e.g. ``POS`` + and ``APEX``. At this stage we will not add any parameter + fields, we will leave this until later. Use the + ``Submit`` button to create the event. + + .. container:: imageblock + + .. container:: content + + |Fill in the necessary information for the + 'SALE_INPUT' event and click 'Submit'| + + .. container:: title + + Figure 7. Populate the ``SALE_INPUT`` event + + .. container:: paragraph + + Repeat the same steps for a new event type called + ``SALE_AUTH``. Just use ``APEX`` as source and ``POS`` as + target, since this is the output event coming from APEX + going to the sales point. + + .. container:: paragraph + + Before we can add parameter fields to an event we must + first define APEX Context Item Schemas that can be used + by those fields. + + .. container:: paragraph + + To create new item schemas, click on the 'Context Item + Schemas' tab. In that 'Context Item Schemas' pane, right + click and select 'Create new ContextSchema'. + + .. container:: imageblock + + .. container:: content + + |Right click to create a new Item Schema| + + .. container:: title + + Figure 8. Create new Data Types + + .. container:: paragraph + + Create item schemas with the following characteristics, + each with its own unique UUID: + + .. table:: Table 6. Item Schemas + + +-------------------+-----------------+-----------------+----------------------+ + | Name | Schema Flavour | Schema | Description | + | | | Definition | | + +===================+=================+=================+======================+ + | timestamp_type | Java | java.lang.Long | A type for | + | | | | ``time`` values | + +-------------------+-----------------+-----------------+----------------------+ + | sale_ID_type | Java | java.lang.Long | A type for | + | | | | ``sale_ID`` | + | | | | values | + +-------------------+-----------------+-----------------+----------------------+ + | price_type | Java | java.lang.Long | A type for | + | | | | ``amount``/``price`` | + | | | | values | + +-------------------+-----------------+-----------------+----------------------+ + | item_ID_type | Java | java.lang.Long | A type for | + | | | | ``item_ID`` | + | | | | values | + +-------------------+-----------------+-----------------+----------------------+ + | assistant_ID_type | Java | java.lang.Long | A type for | + | | | | ``assistant_ID`` | + | | | | values | + +-------------------+-----------------+-----------------+----------------------+ + | quantity_type | Java | java.lang.Integ | A type for | + | | | er | ``quantity`` | + | | | | values | + +-------------------+-----------------+-----------------+----------------------+ + | branch_ID_type | Java | java.lang.Long | A type for | + | | | | ``branch_ID`` | + | | | | values | + +-------------------+-----------------+-----------------+----------------------+ + | notes_type | Java | java.lang.Strin | A type for | + | | | g | ``notes`` | + | | | | values | + +-------------------+-----------------+-----------------+----------------------+ + | authorised_type | Java | java.lang.Boole | A type for | + | | | an | ``authorised`` | + | | | | values | + +-------------------+-----------------+-----------------+----------------------+ + | message_type | Java | java.lang.Strin | A type for | + | | | g | ``message`` | + | | | | values | + +-------------------+-----------------+-----------------+----------------------+ + + .. container:: imageblock + + .. container:: content + + |Create a new Item Schema| + + .. container:: title + + Figure 9. Create new Item Schemas + + .. container:: paragraph + + The item schemas can now be seen on the 'Context Item + Schemas' tab, and can be updated at any time by + right-clicking on the item schemas on the 'Context Item + Schemas' tab. Now we can go back to the event definitions + for ``SALE_INPUT`` and ``SALE_AUTH`` and add some + parameter fields. + + .. tip + + .. container:: title + + Field Schema types + + .. container:: paragraph + + APEX natively supports schema definitions in ``Java`` and ``Avro``. + + .. container:: paragraph + + ``Java`` schema definitions are simply the name of a Java Class. There are some restrictions: + + .. container:: ulist + + - the class must be instantiatable, i.e. not an Java interface or abstract class + + - primitive types are not supported, i.e. use ``java.lang.Integer`` instead of ``int``, etc. + + - it must be possible to find the class, i.e. the class must be contained in the Java classpath. + + .. container:: paragraph + + ``Avro`` schema definitions can be any valid `Avro `__ + schema. For events using fields defined with ``Avro`` schemas, any incoming event containing that field must + contain a value that conforms to the Avro schema. + + .. container:: paragraph + + Click on the 'Events' tab, then right click the + ``SALE_INPUT`` row and select 'Edit Event + :literal:`SALE_INPUT’. To add a new event parameter use the 'Add Event Parameter' button at the bottom of the screen. For the `SALE_INPUT` + event add the following event parameters: + + .. table:: Table 7. Event Parameter Fields for the ``SALE_INPUT`` Event + + +----------------------+----------------------+-----------------------+ + | Parameter Name | Parameter Type | Optional | + +======================+======================+=======================+ + | time | timestamp_type | no | + +----------------------+----------------------+-----------------------+ + | sale_ID | sale_ID_type | no | + +----------------------+----------------------+-----------------------+ + | amount | price_type | no | + +----------------------+----------------------+-----------------------+ + | item_ID | item_ID_type | no | + +----------------------+----------------------+-----------------------+ + | quantity | quantity_type | no | + +----------------------+----------------------+-----------------------+ + | assistant_ID | assistant_ID_type | no | + +----------------------+----------------------+-----------------------+ + | branch_ID | branch_ID_type | no | + +----------------------+----------------------+-----------------------+ + | notes | notes_type | *yes* | + +----------------------+----------------------+-----------------------+ + + .. container:: paragraph + + Remember to click the 'Submit' button at the bottom of + the event definition pane. + + .. tip:: + Optional Fields in APEX Events + Parameter fields can be *optional* in events. If a parameter is not marked as *optional* then by default it + is *mandatory*, so it must appear in any input event passed to APEX. If an *optional* field is not set + for an output event then value will be set to ``null``. + + .. container:: imageblock + + .. container:: content + + |Add new event parameters to an event| + + .. container:: title + + Figure 10. Add typed parameter fields to an event + + .. container:: paragraph + + Select the ``SALE_AUTH`` event and add the following + event parameters: + + .. table:: Table 8. Event Parameter Fields for the ``SALE_AUTH`` Event + + +----------------------+----------------------+-----------------------+ + | Parameter Name | Parameter Type | no | + +======================+======================+=======================+ + | sale_ID | sale_ID_type | no | + +----------------------+----------------------+-----------------------+ + | time | timestamp_type | no | + +----------------------+----------------------+-----------------------+ + | authorised | authorised_type | no | + +----------------------+----------------------+-----------------------+ + | message | message_type | *yes* | + +----------------------+----------------------+-----------------------+ + | amount | price_type | no | + +----------------------+----------------------+-----------------------+ + | item_ID | item_ID_type | no | + +----------------------+----------------------+-----------------------+ + | assistant_ID | assistant_ID_type | no | + +----------------------+----------------------+-----------------------+ + | quantity | quantity_type | no | + +----------------------+----------------------+-----------------------+ + | branch_ID | branch_ID_type | no | + +----------------------+----------------------+-----------------------+ + | notes | notes_type | *yes* | + +----------------------+----------------------+-----------------------+ + + .. container:: paragraph + + Remember to click the 'Submit' button at the bottom of + the event definition pane. + + .. container:: paragraph + + The events for our policy are now defined. + +Create a new Policy and add the *"No Booze before 11:30"* check +############################################################### + + .. container:: paragraph + + APEX policies are defined using a state-machine model. + Each policy comprises one or more *states* that can be + individually executed. Where there is more than one + *state* the states are chained together to form a + `Directed Acyclic Graph + (DAG) `__ + of states. A *state* is triggered by passing it a single + input (or 'trigger') event and once executed each state + then emits an output event. For each *state* the logic + for the *state* is embedded in one or more *tasks*. Each + *task* contains specific *task logic* that is executed by + the APEX execution environment each time the *task* is + invoked. Where there is more than one *task* in a *state* + then the *state* also defines some *task selection logic* + to select an appropriate task each time the *state* is + executed. + + .. container:: paragraph + + Therefore, to create a new policy we must first define + one or more tasks. + + .. container:: paragraph + + To create a new Task click on the 'Tasks' tab. In the + 'Tasks' pane, right click and select 'Create new Task'. + Create a new Task called ``MorningBoozeCheck``. Use the + 'Generate UUID' button to create a new unique ID for the + task, and fill in a description for the task. + + .. container:: imageblock + + .. container:: content + + |Right click to create a new task| + + .. container:: title + + Figure 11. Create a new Task + + .. container:: paragraph + + Tasks are configured with a set of *input fields* and a + set of *output fields*. To add new input/output fields + for a task use the 'Add Task Input Field' and 'Add Task + Output Field' button. The list of input and out fields to + add for the ``MorningBoozeCheck`` task are given below. + The input fields are drawn from the parameters in the + state’s input event, and the task’s output fields are + used to populate the state’s output event. The task’s + input and output fields must be a subset of the event + parameters defined for the input and output events for + any state that uses that task. (You may have noticed that + the input and output fields for the ``MorningBoozeCheck`` + task have the exact same names and reuse the item schemas + that we used for the parameters in the ``SALE_INPUT`` and + ``SALE_AUTH`` events respectively). + + .. table:: Table 9. Input fields for ``MorningBoozeCheck`` task + + +-----------------------------------+-----------------------------------+ + | Parameter Name | Parameter Type | + +===================================+===================================+ + | time | timestamp_type | + +-----------------------------------+-----------------------------------+ + | sale_ID | sale_ID_type | + +-----------------------------------+-----------------------------------+ + | amount | price_type | + +-----------------------------------+-----------------------------------+ + | item_ID | item_ID_type | + +-----------------------------------+-----------------------------------+ + | quantity | quantity_type | + +-----------------------------------+-----------------------------------+ + | assistant_ID | assistant_ID_type | + +-----------------------------------+-----------------------------------+ + | branch_ID | branch_ID_type | + +-----------------------------------+-----------------------------------+ + | notes | notes_type | + +-----------------------------------+-----------------------------------+ + + .. table:: Table 10. Output fields for ``MorningBoozeCheck`` task + + +-----------------------------------+-----------------------------------+ + | Parameter Name | Parameter Type | + +===================================+===================================+ + | sale_ID | sale_ID_type | + +-----------------------------------+-----------------------------------+ + | time | timestamp_type | + +-----------------------------------+-----------------------------------+ + | authorised | authorised_type | + +-----------------------------------+-----------------------------------+ + | message | message_type | + +-----------------------------------+-----------------------------------+ + | amount | price_type | + +-----------------------------------+-----------------------------------+ + | item_ID | item_ID_type | + +-----------------------------------+-----------------------------------+ + | assistant_ID | assistant_ID_type | + +-----------------------------------+-----------------------------------+ + | quantity | quantity_type | + +-----------------------------------+-----------------------------------+ + | branch_ID | branch_ID_type | + +-----------------------------------+-----------------------------------+ + | notes | notes_type | + +-----------------------------------+-----------------------------------+ + + .. container:: imageblock + + .. container:: content + + |Add input and out fields for the task| + + .. container:: title + + Figure 12. Add input and out fields for the Task + + .. container:: paragraph + + Each task must include some 'Task Logic' that implements + the behaviour for the task. Task logic can be defined in + a number of different ways using a choice of languages. + For this task we will author the logic using the + Java-like scripting language called + ```MVEL`` `__. + + .. container:: paragraph + + For simplicity use the following code for the task logic. + Paste the script text into the 'Task Logic' box, and use + "MVEL" as the 'Task Logic Type / Flavour'. + + .. container:: paragraph + + This logic assumes that all items with ``item_ID`` + between 1000 and 2000 contain alcohol, which is not very + realistic, but we will see a better approach for this + later. It also uses the standard ``Java`` time utilities + to check if the current time is between ``00:00:00 GMT`` + and ``11:30:00 GMT``. For a detailed guide to how to + write your own logic in + ```JavaScript`` `__, + ```MVEL`` `__ or one + of the other supported languages please refer to APEX + Programmers Guide. + + .. container:: listingblock + + .. container:: title + + MVEL code for the ``MorningBoozeCheck`` task + + .. container:: content + + .. code:: + + /* + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + import java.util.Date; + import java.util.Calendar; + import java.util.TimeZone; + import java.text.SimpleDateFormat; + + logger.info("Task Execution: '"+subject.id+"'. Input Fields: '"+inFields+"'"); + + outFields.put("amount" , inFields.get("amount")); + outFields.put("assistant_ID", inFields.get("assistant_ID")); + outFields.put("notes" , inFields.get("notes")); + outFields.put("quantity" , inFields.get("quantity")); + outFields.put("branch_ID" , inFields.get("branch_ID")); + outFields.put("item_ID" , inFields.get("item_ID")); + outFields.put("time" , inFields.get("time")); + outFields.put("sale_ID" , inFields.get("sale_ID")); + + item_id = inFields.get("item_ID"); + + //The events used later to test this task use GMT timezone! + gmt = TimeZone.getTimeZone("GMT"); + timenow = Calendar.getInstance(gmt); + df = new SimpleDateFormat("HH:mm:ss z"); + df.setTimeZone(gmt); + timenow.setTimeInMillis(inFields.get("time")); + + midnight = timenow.clone(); + midnight.set( + timenow.get(Calendar.YEAR),timenow.get(Calendar.MONTH), + timenow.get(Calendar.DATE),0,0,0); + eleven30 = timenow.clone(); + eleven30.set( + timenow.get(Calendar.YEAR),timenow.get(Calendar.MONTH), + timenow.get(Calendar.DATE),11,30,0); + + itemisalcohol = false; + if(item_id != null && item_id >=1000 && item_id < 2000) + itemisalcohol = true; + + if( itemisalcohol + && timenow.after(midnight) && timenow.before(eleven30)){ + outFields.put("authorised", false); + outFields.put("message", "Sale not authorised by policy task "+subject.taskName+ + " for time "+df.format(timenow.getTime())+ + ". Alcohol can not be sold between "+df.format(midnight.getTime())+ + " and "+df.format(eleven30.getTime())); + return true; + } + else{ + outFields.put("authorised", true); + outFields.put("message", "Sale authorised by policy task "+subject.taskName+ + " for time "+df.format(timenow.getTime())); + return true; + } + + /* + This task checks if a sale request is for an item that is an alcoholic drink. + If the local time is between 00:00:00 GMT and 11:30:00 GMT then the sale is not + authorised. Otherwise the sale is authorised. + In this implementation we assume that items with item_ID value between 1000 and + 2000 are all alcoholic drinks :-) + */ + + .. container:: imageblock + + .. container:: content + + |Add task logic the task| + + .. container:: title + + Figure 13. Add Task Logic the Task + + .. container:: paragraph + + An alternative version of the same logic is available in + JavaScript. Just use "JAVASCRIPT" as the 'Task Logic Type + / Flavour' instead. + + .. container:: listingblock + + .. container:: title + + Javascript alternative for the ``MorningBoozeCheck`` + task + + .. container:: content + + .. code:: + + /* + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + + var returnValueType = Java.type("java.lang.Boolean"); + var returnValue = new returnValueType(true); + + // Load compatibility script for imports etc + load("nashorn:mozilla_compat.js"); + importPackage(java.text); + importClass(java.text.SimpleDateFormat); + + executor.logger.info("Task Execution: '"+executor.subject.id+"'. Input Fields: '"+executor.inFields+"'"); + + executor.outFields.put("amount" , executor.inFields.get("amount")); + executor.outFields.put("assistant_ID", executor.inFields.get("assistant_ID")); + executor.outFields.put("notes" , executor.inFields.get("notes")); + executor.outFields.put("quantity" , executor.inFields.get("quantity")); + executor.outFields.put("branch_ID" , executor.inFields.get("branch_ID")); + executor.outFields.put("item_ID" , executor.inFields.get("item_ID")); + executor.outFields.put("time" , executor.inFields.get("time")); + executor.outFields.put("sale_ID" , executor.inFields.get("sale_ID")); + + item_id = executor.inFields.get("item_ID"); + + //All times in this script are in GMT/UTC since the policy and events assume time is in GMT. + var timenow_gmt = new Date(Number(executor.inFields.get("time"))); + + var midnight_gmt = new Date(Number(executor.inFields.get("time"))); + midnight_gmt.setUTCHours(0,0,0,0); + + var eleven30_gmt = new Date(Number(executor.inFields.get("time"))); + eleven30_gmt.setUTCHours(11,30,0,0); + + var timeformatter = new java.text.SimpleDateFormat("HH:mm:ss z"); + + var itemisalcohol = false; + if(item_id != null && item_id >=1000 && item_id < 2000) + itemisalcohol = true; + + if( itemisalcohol + && timenow_gmt.getTime() >= midnight_gmt.getTime() + && timenow_gmt.getTime() < eleven30_gmt.getTime()) { + + executor.outFields.put("authorised", false); + executor.outFields.put("message", "Sale not authorised by policy task " + + executor.subject.taskName+ " for time " + timeformatter.format(timenow_gmt.getTime()) + + ". Alcohol can not be sold between " + timeformatter.format(midnight_gmt.getTime()) + + " and " + timeformatter.format(eleven30_gmt.getTime())); + } + else{ + executor.outFields.put("authorised", true); + executor.outFields.put("message", "Sale authorised by policy task " + + executor.subject.taskName + " for time "+timeformatter.format(timenow_gmt.getTime())); + } + + /* + This task checks if a sale request is for an item that is an alcoholic drink. + If the local time is between 00:00:00 GMT and 11:30:00 GMT then the sale is not + authorised. Otherwise the sale is authorised. + In this implementation we assume that items with item_ID value between 1000 and + 2000 are all alcoholic drinks :-) + */ + + .. container:: paragraph + + The task definition is now complete so click the 'Submit' + button to save the task. The task can now be seen on the + 'Tasks' tab, and can be updated at any time by + right-clicking on the task on the 'Task' tab. Now that we + have created our task, we can can create a policy that + uses that task. + + .. container:: paragraph + + To create a new Policy click on the 'Policies' tab. In + the 'Policies' pane, right click and select 'Create new + Policy': + + .. container:: paragraph + + Create a new Policy called ``MyFirstPolicy``. Use the + 'Generate UUID' button to create a new unique ID for the + policy, and fill in a description for the policy. Use + 'FREEFORM' as the 'Policy Flavour'. + + .. container:: paragraph + + Each policy must have at least one state. Since this is + 'freeform' policy we can add as many states as we wish. + Let’s start with one state. Add a new state called + ``BoozeAuthDecide`` to this ``MyFirstPolicy`` policy + using the 'Add new State' button after filling in the + name of our new state. + + .. container:: imageblock + + .. container:: content + + |Create a new policy| + + .. container:: title + + Figure 14. Create a new Policy + + .. container:: paragraph + + Each state must uses one input event type. For this new + state select the ``SALE_INPUT`` event as the input event. + + .. container:: paragraph + + Each policy must define a 'First State' and a 'Policy + Trigger Event'. The 'Policy Trigger Event' is the input + event for the policy as a whole. This event is then + passed to the first state in the chain of states in the + policy, therefore the 'Policy Trigger Event' will be the + input event for the first state. Each policy can only + have one 'First State'. For our ``MyFirstPolicy`` policy, + select ``BoozeAuthDecide`` as the 'First State'. This + will automatically select ``SALE_INPUT`` as the 'Policy + Trigger Event' for our policy. + + .. container:: imageblock + + .. container:: content + + |Create a state| + + .. container:: title + + Figure 15. Create a new State + + .. container:: paragraph + + In this case we will create a reference the pre-existing + ``MorningBoozeCheck`` task that we defined above using + the 'Add New Task' button. Select the + ``MorningBoozeCheck`` task, and use the name of the task + as the 'Local Name' for the task. + + .. container:: paragraph + + in the case where a state references more than one task, + a 'Default Task' must be selected for the state and some + logic ('Task Selection Logic') must be specified to + select the appropriate task at execution time. Since our + new state ``BoozeAuthDecide`` only has one task the + default task is automatically selected and no 'Task + Selection Logic' is required. + + .. note:: + .. container:: title + + State Output Mappings + + .. container:: paragraph + + In a 'Policy' 'State' a 'State Output Mapping' has 3 roles: + 1) Select which 'State' should be executed next, 2) Select + the type of the state’s 'Outgoing Event', and 3) + Populate the state’s 'Outgoing Event'. This is how states are + chained together to form a (`Directed Acyclic Graph + (DAG) `__ ) + of states. The final state(s) of a policy are those that do + not select any 'next' state. Since a 'State' can only + accept a single type of event, the type of the event emitted + by a previous 'State' must be match the incoming event type + of the next 'State'. This is also how the last state(s) in + a policy can emit events of different types. The 'State + Output Mapping' is also responsible for taking the + fields that are output by the task executed in the state and + populating the state’s output event before it is emitted. + + .. container:: paragraph + + Each 'Task' referenced in 'State' must have a defined + 'Output Mapping' to take the output of the task, select an + 'Outgoing Event' type for the state, populate the state’s + outgoing event, and then select the next state to be + executed (if any). + + .. container:: paragraph + + There are 2 basic types of output mappings: + + .. container:: olist arabic + + #. **Direct Output Mappings** have a single value for + 'Next State' and a single value for 'State Output + Event'. The outgoing event for the state is + automatically created, any outgoing event parameters + that were present in the incoming event are copied + into the outgoing event, then any task output fields + that have the same name and type as parameters in the + outgoing event are automatically copied into + the outgoing event. + + #. **Logic-based State Output Mappings / Finalizers** + have some logic defined that dynamically selects + and creates the 'State Outgoing Event', manages + the population of the outgoing event parameters + (perhaps changing or adding to the outputs from the + task), and then dynamically selects the next state to + be executed (if any). + + .. container:: paragraph + + Each task reference must also have an associated 'Output + State Mapping' so we need an 'Output State Mapping' for + the ``BoozeAuthDecide`` state to use when the + ``MorningBoozeCheck`` task is executed. The simplest type + of output mapping is a 'Direct Output Mapping'. + + .. container:: paragraph + + Create a new 'Direct Output Mapping' for the state called + ``MorningBoozeCheck_Output_Direct`` using the 'Add New + Direct State Output Mapping' button. Select ``SALE_AUTH`` + as the output event and select ``None`` for the next + state value. We can then select this output mapping for + use when the the ``MorningBoozeCheck`` task is executed. + Since there is only state, and only one task for that + state, this output mapping ensures that the + ``BoozeAuthDecide`` state is the only state executed and + the state (and the policy) can only emit events of type + ``SALE_AUTH``. (You may remember that the output fields + for the ``MorningBoozeCheck`` task have the exact same + names and reuse the item schemas that we used for the + parameters in ``SALE_AUTH`` event. The + ``MorningBoozeCheck_Output_Direct`` direct output mapping + can now automatically copy the values from the + ``MorningBoozeCheck`` task directly into outgoing + ``SALE_AUTH`` events.) + + .. container:: imageblock + + .. container:: content + + |Add a Task and Output Mapping| + + .. container:: title + + Figure 16. Add a Task and Output Mapping + + .. container:: paragraph + + Click the 'Submit' button to complete the definition of + our ``MyFirstPolicy`` policy. The policy + ``MyFirstPolicy`` can now be seen in the list of policies + on the 'Policies' tab, and can be updated at any time by + right-clicking on the policy on the 'Policies' tab. + + .. container:: paragraph + + The ``MyFirstPolicyModel``, including our + ``MyFirstPolicy`` policy can now be checked for errors. + Click on the 'Model' menu and select 'Validate'. The + model should validate without any 'Warning' or 'Error' + messages. If you see any 'Error' or 'Warning' messages, + carefully read the message as a hint to find where you + might have made a mistake when defining some aspect of + your policy model. + + .. container:: imageblock + + .. container:: content + + |Validate the policy model for error using the 'Model' + > 'Validate' menu item| + + .. container:: title + + Figure 17. Validate a Policy Model + + .. container:: paragraph + + Congratulations, you have now completed your first APEX + policy. The policy model containing our new policy can + now be exported from the editor and saved. Click on the + 'File' menu and select 'Download' to save the policy + model in JSON format. The exported policy model is then + available in the directory you selected, for instance + ``$APEX_HOME/examples/models/MyFirstPolicy/1/MyFirstPolicyModel_0.0.1.json``. + The exported policy can now be loaded into the APEX + Policy Engine, or can be re-loaded and edited by the APEX + Policy Editor. + + .. container:: imageblock + + .. container:: content + + |Download the completed policy model using the 'File' + > 'Download' menu item| + + .. container:: title + + Figure 18. Export a Policy Model + +Test Policy Step 1 +################## + + .. container:: paragraph + + To start a new APEX Engine you can use the following + configuration. In a full APEX installation you can find + this configuration in + ``$APEX_HOME/examples/config/MyFirstPolicy/1/MyFirstPolicyConfigStdin2StdoutJsonEvent.json``. + This configuration expects incoming events to be in + ``JSON`` format and to be passed into the APEX Engine + from ``stdin``, and result events will be printed in + ``JSON`` format to ``stdout``. This configuration loads + the policy model stored in the file + 'MyFirstPolicyModel_0.0.1.json' as exported from the APEX + Editor. Note, you may need to edit this file to provide + the full path to wherever you stored the exported policy + model file. + + .. container:: listingblock + + .. container:: title + + JSON to load and execute *My First Policy*, read input + JSON events from ``stdin``, and emit output events to + ``stdout`` + + .. container:: content + + .. code:: + + { + "engineServiceParameters" : { + "name" : "MyFirstPolicyApexEngine", + "version" : "0.0.1", + "id" : 101, + "instanceCount" : 4, + "deploymentPort" : 12345, + "policyModelFileName" : "examples/models/MyFirstPolicy/1/MyFirstPolicyModel_0.0.1.json", + "engineParameters" : { + "executorParameters" : { + "MVEL" : { + "parameterClassName" : "org.onap.policy.apex.plugins.executor.mvel.MVELExecutorParameters" + }, + "JAVASCRIPT" : { + "parameterClassName" : "org.onap.policy.apex.plugins.executor.javascript.JavascriptExecutorParameters" + } + } + } + }, + "eventOutputParameters": { + "FirstProducer": { + "carrierTechnologyParameters" : { + "carrierTechnology" : "FILE", + "parameters" : { + "standardIO" : true + } + }, + "eventProtocolParameters" : { + "eventProtocol" : "JSON" + } + } + }, + "eventInputParameters": { + "FirstConsumer": { + "carrierTechnologyParameters" : { + "carrierTechnology" : "FILE", + "parameters" : { + "standardIO" : true + } + }, + "eventProtocolParameters" : { + "eventProtocol" : "JSON" + } + } + } + } + + .. container:: paragraph + + To test the policy try paste the following events into + the console as the APEX engine executes: + + .. table:: Table 11. Inputs and Outputs when testing *My First Policy* + + +------------------------------------------+-------------------------------------------+-----------+ + | Input Event (JSON) | Output Event (JSON) | comment | + +==========================================+===========================================+===========+ + | .. container:: | .. container:: | Request | + | | | to buy a | + | .. container:: listingblock | .. container:: listingblock | non-alcoh | + | | | olic | + | | .. container:: content | item | + | .. container:: content | | (``item_I | + | | .. code:: | D=5123``) | + | | | at | + | .. code:: | { | *10:13:09 | + | | "name": "SALE_AUTH", | * | + | | | on | + | { | "version": "0.0.1", | *Tuesday, | + | "nameSpace": "com.hyperm", | "nameSpace": "com.hyperm", | 10 | + | "name" : "SALE_INPUT", | "source": "", | January | + | "version": "0.0.1", | "target": "", | 2017*. | + | "time" : 1483351989000, | "amount": 299, | Sale is | + | "sale_ID": 99999991, | "assistant_ID": 23, | authorize | + | "amount": 299, | "authorised": true, | d. | + | "item_ID": 5123, | "branch_ID": 1, | | + | "quantity": 1, | "item_ID": 5123, | | + | "assistant_ID": 23, | "message": "Sale authorised | | + | "branch_ID": 1, | by policy task MorningBo | | + | "notes": "Special Offer!!" | ozeCheck for time 10:13:09 | | + | } | GMT", | | + | | "notes": "Special Offer!!", | | + | | "quantity": 1, | | + | | "sale_ID": 99999991, | | + | | "time": 1483351989000 | | + | | } | | + | | | | + | | | | + | | | | + +------------------------------------------+-------------------------------------------+-----------+ + | .. container:: | .. container:: | Request | + | | | to buy | + | .. container:: listingblock | .. container:: listingblock | alcohol | + | | | item | + | .. container:: content | .. container:: content | (``item_I | + | | | D=1249``) | + | .. code:: | .. code:: | at | + | | | *08:41:06 | + | { | { | * | + | "nameSpace": "com.hyperm", | "nameSpace": "com.hyperm", | on | + | "name": "SALE_INPUT", | "name": "SALE_AUTH", | *Monday, | + | "version": "0.0.1", | "source": "", | 02 | + | "time": 1483346466000, | "target": "", | January | + | "sale_ID": 99999992, | "amount": 1249, | 2017*. | + | "version": "0.0.1", | "assistant_ID": 12, | | + | "amount": 1249, | "authorised": false, | Sale is | + | "item_ID": 1012, | "branch_ID": 2, | not | + | "quantity": 1, | "item_ID": 1012, | authorize | + | "assistant_ID": 12, | "message": "Sale not | d. | + | "branch_ID": 2 | authorised by policy task | | + | } | MorningBoozeCheck for time | | + | | 08:41:06 GMT. Alcohol can | | + | | not be sold between | | + | | 00:00:00 GMT and 11:30:00 | | + | | GMT", | | + | | "notes": null, | | + | | "quantity": 1, | | + | | "sale_ID": 99999992, | | + | | "time": 1483346466000 | | + | | } | | + +------------------------------------------+-------------------------------------------+-----------+ + | .. container:: | .. container:: | Request | + | | | to buy | + | .. container:: listingblock | .. container:: listingblock | alcohol | + | | | (``item_I | + | | .. container:: content | D=1943``) | + | .. container:: content | | at | + | | .. code:: | *20:17:13 | + | | | * | + | .. code:: | { | on | + | | "name": "SALE_AUTH", | *Tuesday, | + | { | "version": "0.0.1", | 20 | + | "nameSpace": "com.hyperm", | "nameSpace": "com.hyperm", | December | + | "name" : "SALE_INPUT", | "source": "", | 2016*. | + | "version": "0.0.1", | "target": "", | | + | "time" : 1482265033000, | "amount": 4799, | Sale is | + | "sale_ID": 99999993, | "assistant_ID": 9, | authorize | + | "amount": 4799, | "authorised": true, | d. | + | "item_ID": 1943, | "branch_ID": 3, | | + | "quantity": 2, | "item_ID": 1943, | | + | "assistant_ID": 9, | "message": "Sale authorised | | + | "branch_ID": 3 | by policy task MorningBo | | + | } | ozeCheck for time 20:17:13 | | + | | GMT", | | + | | "notes": null, | | + | | "quantity": 2, | | + | | "sale_ID": 99999993, | | + | | "time": 1482265033000 | | + | | } | | + +------------------------------------------+-------------------------------------------+-----------+ + +4.3.6. Policy 1 in CLI Editor +############################# + + .. container:: paragraph + + An equivalent version of the ``MyFirstPolicyModel`` + policy model can again be generated using the APEX CLI + editor. A sample APEX CLI script is shown below: + + .. container:: listingblock + + .. container:: title + + APEX CLI Editor code for Policy 1 + + .. container:: content + + .. code:: + + #------------------------------------------------------------------------------- + # ============LICENSE_START======================================================= + # Copyright (C) 2016-2018 Ericsson. All rights reserved. + # ================================================================================ + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + # + # SPDX-License-Identifier: Apache-2.0 + # ============LICENSE_END========================================================= + #------------------------------------------------------------------------------- + + model create name=MyFirstPolicyModel version=0.0.1 uuid=540226fb-55ee-4f0e-a444-983a0494818e description="This is my first Apex Policy Model." + + schema create name=assistant_ID_type version=0.0.1 uuid=36df4c71-9616-4206-8b53-976a5cd4bd87 description="A type for 'assistant_ID' values" flavour=Java schema=java.lang.Long + + schema create name=authorised_type version=0.0.1 uuid=d48b619e-d00d-4008-b884-02d76ea4350b description="A type for 'authorised' values" flavour=Java schema=java.lang.Boolean + + schema create name=branch_ID_type version=0.0.1 uuid=6468845f-4122-4128-8e49-0f52c26078b5 description="A type for 'branch_ID' values" flavour=Java schema=java.lang.Long + + schema create name=item_ID_type version=0.0.1 uuid=4f227ff1-aee0-453a-b6b6-9a4b2e0da932 description="A type for 'item_ID' values" flavour=Java schema=java.lang.Long + + schema create name=message_type version=0.0.1 uuid=ad1431bb-3155-4e73-b5a3-b89bee498749 description="A type for 'message' values" flavour=Java schema=java.lang.String + + schema create name=notes_type version=0.0.1 uuid=eecfde90-896c-4343-8f9c-2603ced94e2d description="A type for 'notes' values" flavour=Java schema=java.lang.String + + schema create name=price_type version=0.0.1 uuid=52c2fc45-fd8c-463c-bd6f-d91b0554aea7 description="A type for 'amount'/'price' values" flavour=Java schema=java.lang.Long + + schema create name=quantity_type version=0.0.1 uuid=ac3d9842-80af-4a98-951c-bd79a431c613 description="A type for 'quantity' values" flavour=Java schema=java.lang.Integer + + schema create name=sale_ID_type version=0.0.1 uuid=cca47d74-7754-4a61-b163-ca31f66b157b description="A type for 'sale_ID' values" flavour=Java schema=java.lang.Long + + schema create name=timestamp_type version=0.0.1 uuid=fd594e88-411d-4a94-b2be-697b3a0d7adf description="A type for 'time' values" flavour=Java schema=java.lang.Long + + task create name=MorningBoozeCheck version=0.0.1 uuid=3351b0f4-cf06-4fa2-8823-edf67bd30223 description=LS + This task checks if the sales request is for an item that contains alcohol. + If the local time is between 00:00:00 and 11:30:00 then the sale is not authorised. Otherwise the sale is authorised. + In this implementation we assume that all items with item_ID values between 1000 and 2000 contain alcohol :-) + LE + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=sale_ID schemaName=sale_ID_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=amount schemaName=price_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=assistant_ID schemaName=assistant_ID_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=notes schemaName=notes_type schemaVersion=0.0.1 optional=true + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=quantity schemaName=quantity_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=branch_ID schemaName=branch_ID_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=item_ID schemaName=item_ID_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=time schemaName=timestamp_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=sale_ID schemaName=sale_ID_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=amount schemaName=price_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=assistant_ID schemaName=assistant_ID_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=notes schemaName=notes_type schemaVersion=0.0.1 optional=true + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=quantity schemaName=quantity_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=branch_ID schemaName=branch_ID_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=item_ID schemaName=item_ID_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=authorised schemaName=authorised_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=time schemaName=timestamp_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=message schemaName=message_type schemaVersion=0.0.1 optional=true + task logic create name=MorningBoozeCheck version=0.0.1 logicFlavour=MVEL logic=LS + /* + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + import java.util.Date; + import java.util.Calendar; + import java.util.TimeZone; + import java.text.SimpleDateFormat; + + logger.info("Task Execution: '"+subject.id+"'. Input Fields: '"+inFields+"'"); + + outFields.put("amount" , inFields.get("amount")); + outFields.put("assistant_ID", inFields.get("assistant_ID")); + outFields.put("notes" , inFields.get("notes")); + outFields.put("quantity" , inFields.get("quantity")); + outFields.put("branch_ID" , inFields.get("branch_ID")); + outFields.put("item_ID" , inFields.get("item_ID")); + outFields.put("time" , inFields.get("time")); + outFields.put("sale_ID" , inFields.get("sale_ID")); + + item_id = inFields.get("item_ID"); + + //The events used later to test this task use GMT timezone! + gmt = TimeZone.getTimeZone("GMT"); + timenow = Calendar.getInstance(gmt); + df = new SimpleDateFormat("HH:mm:ss z"); + df.setTimeZone(gmt); + timenow.setTimeInMillis(inFields.get("time")); + + midnight = timenow.clone(); + midnight.set( + timenow.get(Calendar.YEAR),timenow.get(Calendar.MONTH), + timenow.get(Calendar.DATE),0,0,0); + eleven30 = timenow.clone(); + eleven30.set( + timenow.get(Calendar.YEAR),timenow.get(Calendar.MONTH), + timenow.get(Calendar.DATE),11,30,0); + + itemisalcohol = false; + if(item_id != null && item_id >=1000 && item_id < 2000) + itemisalcohol = true; + + if( itemisalcohol + && timenow.after(midnight) && timenow.before(eleven30)){ + outFields.put("authorised", false); + outFields.put("message", "Sale not authorised by policy task "+subject.taskName+ + " for time "+df.format(timenow.getTime())+ + ". Alcohol can not be sold between "+df.format(midnight.getTime())+ + " and "+df.format(eleven30.getTime())); + return true; + } + else{ + outFields.put("authorised", true); + outFields.put("message", "Sale authorised by policy task "+subject.taskName+ + " for time "+df.format(timenow.getTime())); + return true; + } + + /* + This task checks if a sale request is for an item that is an alcoholic drink. + If the local time is between 00:00:00 GMT and 11:30:00 GMT then the sale is not + authorised. Otherwise the sale is authorised. + In this implementation we assume that items with item_ID value between 1000 and + 2000 are all alcoholic drinks :-) + */ + LE + + event create name=SALE_AUTH version=0.0.1 uuid=c4500941-3f98-4080-a9cc-5b9753ed050b description="An event emitted by the Policy to indicate whether the sale of an item has been authorised" nameSpace=com.hyperm source="APEX" target="POS" + event parameter create name=SALE_AUTH version=0.0.1 parName=amount schemaName=price_type schemaVersion=0.0.1 + event parameter create name=SALE_AUTH version=0.0.1 parName=assistant_ID schemaName=assistant_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_AUTH version=0.0.1 parName=authorised schemaName=authorised_type schemaVersion=0.0.1 + event parameter create name=SALE_AUTH version=0.0.1 parName=branch_ID schemaName=branch_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_AUTH version=0.0.1 parName=item_ID schemaName=item_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_AUTH version=0.0.1 parName=message schemaName=message_type schemaVersion=0.0.1 optional=true + event parameter create name=SALE_AUTH version=0.0.1 parName=notes schemaName=notes_type schemaVersion=0.0.1 optional=true + event parameter create name=SALE_AUTH version=0.0.1 parName=quantity schemaName=quantity_type schemaVersion=0.0.1 + event parameter create name=SALE_AUTH version=0.0.1 parName=sale_ID schemaName=sale_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_AUTH version=0.0.1 parName=time schemaName=timestamp_type schemaVersion=0.0.1 + + event create name=SALE_INPUT version=0.0.1 uuid=4f04aa98-e917-4f4a-882a-c75ba5a99374 description="An event raised by the PoS system each time an item is scanned for purchase" nameSpace=com.hyperm source="POS" target="APEX" + event parameter create name=SALE_INPUT version=0.0.1 parName=amount schemaName=price_type schemaVersion=0.0.1 + event parameter create name=SALE_INPUT version=0.0.1 parName=assistant_ID schemaName=assistant_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_INPUT version=0.0.1 parName=branch_ID schemaName=branch_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_INPUT version=0.0.1 parName=item_ID schemaName=item_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_INPUT version=0.0.1 parName=notes schemaName=notes_type schemaVersion=0.0.1 optional=true + event parameter create name=SALE_INPUT version=0.0.1 parName=quantity schemaName=quantity_type schemaVersion=0.0.1 + event parameter create name=SALE_INPUT version=0.0.1 parName=sale_ID schemaName=sale_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_INPUT version=0.0.1 parName=time schemaName=timestamp_type schemaVersion=0.0.1 + + + policy create name=MyFirstPolicy version=0.0.1 uuid=6c5e410f-489a-46ff-964e-982ce6e8b6d0 description="This is my first Apex policy. It checks if a sale should be authorised or not." template=FREEFORM firstState=BoozeAuthDecide + policy state create name=MyFirstPolicy version=0.0.1 stateName=BoozeAuthDecide triggerName=SALE_INPUT triggerVersion=0.0.1 defaultTaskName=MorningBoozeCheck defaultTaskVersion=0.0.1 + policy state output create name=MyFirstPolicy version=0.0.1 stateName=BoozeAuthDecide outputName=MorningBoozeCheck_Output_Direct eventName=SALE_AUTH eventVersion=0.0.1 nextState=NULL + policy state taskref create name=MyFirstPolicy version=0.0.1 stateName=BoozeAuthDecide taskLocalName=MorningBoozeCheck taskName=MorningBoozeCheck taskVersion=0.0.1 outputType=DIRECT outputName=MorningBoozeCheck_Output_Direct + +Policy Step 2 +------------- + +Scenario +######### + .. container:: paragraph + + *HyperM* have just opened a new branch in a different + country, but that country has different rules about when + alcohol can be sold! In this section we will go through + the necessary steps to extend our policy to enforce this + for *HyperM*. + + .. container:: ulist + + - In some branches alcohol cannot be sold before 1pm, + and not at all on Sundays. + + .. container:: paragraph + + Although there are a number of ways to accomplish this + the easiest approach for us is to define another task and + then select which task is appropriate at runtime + depending on the branch identifier in the incoming event. + +Extend the Policy with the new Scenario +####################################### + + .. container:: paragraph + + To create a new Task click on the 'Tasks' tab. In the + 'Tasks' pane, right click and select 'Create new Task': + + .. container:: paragraph + + Create a new Task called ``MorningBoozeCheckAlt1``. Use + the 'Generate UUID' button to create a new unique ID for + the task, and fill in a description for the task. Select + the same input and output fields that we used earlier + when we defined the ``MorningBoozeCheck`` task earlier. + + .. table:: Table 12. Input fields for ``MorningBoozeCheckAlt1`` task + + +-----------------------------------+-----------------------------------+ + | Parameter Name | Parameter Type | + +===================================+===================================+ + | time | timestamp_type | + +-----------------------------------+-----------------------------------+ + | sale_ID | sale_ID_type | + +-----------------------------------+-----------------------------------+ + | amount | price_type | + +-----------------------------------+-----------------------------------+ + | item_ID | item_ID_type | + +-----------------------------------+-----------------------------------+ + | quantity | quantity_type | + +-----------------------------------+-----------------------------------+ + | assistant_ID | assistant_ID_type | + +-----------------------------------+-----------------------------------+ + | branch_ID | branch_ID_type | + +-----------------------------------+-----------------------------------+ + | notes | notes_type | + +-----------------------------------+-----------------------------------+ + + .. table:: Table 13. Output fields for ``MorningBoozeCheckAlt1`` task + + +-----------------------------------+-----------------------------------+ + | Parameter Name | Parameter Type | + +===================================+===================================+ + | sale_ID | sale_ID_type | + +-----------------------------------+-----------------------------------+ + | time | timestamp_type | + +-----------------------------------+-----------------------------------+ + | authorised | authorised_type | + +-----------------------------------+-----------------------------------+ + | message | message_type | + +-----------------------------------+-----------------------------------+ + | amount | price_type | + +-----------------------------------+-----------------------------------+ + | item_ID | item_ID_type | + +-----------------------------------+-----------------------------------+ + | assistant_ID | assistant_ID_type | + +-----------------------------------+-----------------------------------+ + | quantity | quantity_type | + +-----------------------------------+-----------------------------------+ + | branch_ID | branch_ID_type | + +-----------------------------------+-----------------------------------+ + | notes | notes_type | + +-----------------------------------+-----------------------------------+ + + .. container:: paragraph + + This task also requires some 'Task Logic' to implement + the new behaviour for this task. + + .. container:: paragraph + + For simplicity use the following code for the task logic. + It again assumes that all items with ``item_ID`` between + 1000 and 2000 contain alcohol. We again use the standard + ``Java`` time utilities to check if the current time is + between ``00:00:00 CET`` and ``13:00:00 CET`` or if it is + ``Sunday``. + + .. container:: paragraph + + For this task we will again author the logic using the + ```MVEL`` `__ + scripting language. Sample task logic code (specified in + ```MVEL`` `__) is + given below. For a detailed guide to how to write your + own logic in + ```JavaScript`` `__, + ```MVEL`` `__ or one + of the other supported languages please refer to APEX + Programmers Guide. + + .. container:: listingblock + + .. container:: title + + MVEL code for the ``MorningBoozeCheckAlt1`` task + + .. container:: content + + .. code:: + + /* + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + import java.util.Date; + import java.util.Calendar; + import java.util.TimeZone; + import java.text.SimpleDateFormat; + + logger.info("Task Execution: '"+subject.id+"'. Input Event: '"+inFields+"'"); + + outFields.put("amount" , inFields.get("amount")); + outFields.put("assistant_ID", inFields.get("assistant_ID")); + outFields.put("notes" , inFields.get("notes")); + outFields.put("quantity" , inFields.get("quantity")); + outFields.put("branch_ID" , inFields.get("branch_ID")); + outFields.put("item_ID" , inFields.get("item_ID")); + outFields.put("time" , inFields.get("time")); + outFields.put("sale_ID" , inFields.get("sale_ID")); + + item_id = inFields.get("item_ID"); + + //The events used later to test this task use CET timezone! + cet = TimeZone.getTimeZone("CET"); + timenow = Calendar.getInstance(cet); + df = new SimpleDateFormat("HH:mm:ss z"); + df.setTimeZone(cet); + timenow.setTimeInMillis(inFields.get("time")); + + midnight = timenow.clone(); + midnight.set( + timenow.get(Calendar.YEAR),timenow.get(Calendar.MONTH), + timenow.get(Calendar.DATE),0,0,0); + onepm = timenow.clone(); + onepm.set( + timenow.get(Calendar.YEAR),timenow.get(Calendar.MONTH), + timenow.get(Calendar.DATE),13,0,0); + + itemisalcohol = false; + if(item_id != null && item_id >=1000 && item_id < 2000) + itemisalcohol = true; + + if( itemisalcohol && + ( (timenow.after(midnight) && timenow.before(onepm)) + || + (timenow.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) + )){ + outFields.put("authorised", false); + outFields.put("message", "Sale not authorised by policy task "+subject.taskName+ + " for time "+df.format(timenow.getTime())+ + ". Alcohol can not be sold between "+df.format(midnight.getTime())+ + " and "+df.format(onepm.getTime()) +" or on Sunday"); + return true; + } + else{ + outFields.put("authorised", true); + outFields.put("message", "Sale authorised by policy task "+subject.taskName+ + " for time "+df.format(timenow.getTime())); + return true; + } + + /* + This task checks if a sale request is for an item that is an alcoholic drink. + If the local time is between 00:00:00 CET and 13:00:00 CET then the sale is not authorised. + Also alcohol sales are not allowed on Sundays. Otherwise the sale is authorised. + In this implementation we assume that items with item_ID between 1000 and 2000 are all alcoholic drinks :-) + */ + + .. container:: imageblock + + .. container:: content + + |Create a new alternative task MorningBoozeCheckAlt1| + + .. container:: title + + Figure 19. Create a new Task + + .. container:: paragraph + + The task definition is now complete so click the 'Submit' + button to save the task. Now that we have created our + task, we can can add this task to the single pre-existing + state (``BoozeAuthDecide``) in our policy. + + .. container:: paragraph + + To edit the ``BoozeAuthDecide`` state in our policy click + on the 'Policies' tab. In the 'Policies' pane, right + click on our ``MyFirstPolicy`` policy and select 'Edit'. + Navigate to the ``BoozeAuthDecide`` state in the 'states' + section at the bottom of the policy definition pane. + + .. container:: imageblock + + .. container:: content + + |Right click to edit a policy| + + .. container:: title + + Figure 20. Edit a Policy + + .. container:: paragraph + + To add our new task ``MorningBoozeCheckAlt1``, scroll + down to the ``BoozeAuthDecide`` state in the 'States' + section. In the 'State Tasks' section for + ``BoozeAuthDecide`` use the 'Add new task' button. Select + our new ``MorningBoozeCheckAlt1`` task, and use the name + of the task as the 'Local Name' for the task. The + ``MorningBoozeCheckAlt1`` task can reuse the same + ``MorningBoozeCheck_Output_Direct`` 'Direct State Output + Mapping' that we used for the ``MorningBoozeCheck`` task. + (Recall that the role of the 'State Output Mapping' is to + select the output event for the state, and select the + next state to be executed. These both remain the same as + before.) + + .. container:: paragraph + + Since our state has more than one task we must define + some logic to determine which task should be used each + time the state is executed. This *task selection logic* + is defined in the state definition. For our + ``BoozeAuthDecide`` state we want the choice of which + task to use to be based on the ``branch_ID`` from which + the ``SALE_INPUT`` event originated. For simplicity sake + let us assume that branches with ``branch_ID`` between + ``0`` and ``999`` should use the ``MorningBoozeCheck`` + task, and the branches with with ``branch_ID`` between + ``1000`` and ``1999`` should use the + ``MorningBoozeCheckAlt1`` task. + + .. container:: paragraph + + This time, for variety, we will author the task selection + logic using the + ```JavaScript`` `__ + scripting language. Sample task selection logic code + (specified in + ```JavaScript`` `__) + is given below. Paste the script text into the 'Task + Selection Logic' box, and use "JAVASCRIPT" as the 'Task + Selection Logic Type / Flavour'. It is necessary to mark + one of the tasks as the 'Default Task' so that the task + selection logic always has a fallback default option in + cases where a particular task cannot be selected. In this + case the ``MorningBoozeCheck`` task can be the default + task. + + .. container:: listingblock + + .. container:: title + + JavaScript code for the ``BoozeAuthDecide`` task + selection logic + + .. container:: content + + .. code:: + + /* + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + + + var returnValueType = Java.type("java.lang.Boolean"); + var returnValue = new returnValueType(true); + + executor.logger.info("Task Selection Execution: '"+executor.subject.id+ + "'. Input Event: '"+executor.inFields+"'"); + + branchid = executor.inFields.get("branch_ID"); + taskorig = executor.subject.getTaskKey("MorningBoozeCheck"); + taskalt = executor.subject.getTaskKey("MorningBoozeCheckAlt1"); + taskdef = executor.subject.getDefaultTaskKey(); + + if(branchid >=0 && branchid <1000){ + taskorig.copyTo(executor.selectedTask); + } + else if (branchid >=1000 && branchid <2000){ + taskalt.copyTo(executor.selectedTask); + } + else{ + taskdef.copyTo(executor.selectedTask); + } + + /* + This task selection logic selects task "MorningBoozeCheck" for branches with + 0<=branch_ID<1000 and selects task "MorningBoozeCheckAlt1" for branches with + 1000<=branch_ID<2000. Otherwise the default task is selected. + In this case the default task is also "MorningBoozeCheck" + */ + + .. container:: imageblock + + .. container:: content + + |State definition with 2 Tasks and Task Selection + Logic| + + .. container:: title + + Figure 21. State definition with 2 Tasks and Task + Selection Logic + + .. container:: paragraph + + When complete don’t forget to click the 'Submit' button + at the bottom of 'Policies' pane for our + ``MyFirstPolicy`` policy after updating the + ``BoozeAuthDecide`` state. + + .. container:: paragraph + + Congratulations, you have now completed the second step + towards your first APEX policy. The policy model + containing our new policy can again be validated and + exported from the editor and saved as shown in Step 1. + + .. container:: paragraph + + The exported policy model is then available in the + directory you selected, as + `MyFirstPolicyModel_0.0.1.json `__. + The exported policy can now be loaded into the APEX + Policy Engine, or can be re-loaded and edited by the APEX + Policy Editor. + +Test Policy Step 2 +################## + + .. container:: paragraph + + To start a new APEX Engine you can use the following + configuration. In a full APEX installation you can find + this configuration in + ``$APEX_HOME/examples/config/MyFirstPolicy/2/MyFirstPolicyConfigStdin2StdoutJsonEvent.json``. + Note, this has changed from the configuration file in + Step 1 to enable the ``JAVASCRIPT`` executor for our new + 'Task Selection Logic'. + + .. container:: listingblock + + .. container:: title + + JSON to load and execute *My First Policy*, read input + JSON events from ``stdin``, and emit output events to + ``stdout`` + + .. container:: content + + .. code:: + + { + "engineServiceParameters" : { + "name" : "MyFirstPolicyApexEngine", + "version" : "0.0.1", + "id" : 102, + "instanceCount" : 4, + "deploymentPort" : 12345, + "policyModelFileName" : "examples/models/MyFirstPolicy/2/MyFirstPolicyModel_0.0.1.json", + "engineParameters" : { + "executorParameters" : { + "MVEL" : { + "parameterClassName" : "org.onap.policy.apex.plugins.executor.mvel.MVELExecutorParameters" + }, + "JAVASCRIPT" : { + "parameterClassName" : "org.onap.policy.apex.plugins.executor.javascript.JavascriptExecutorParameters" + } + } + } + }, + "eventOutputParameters": { + "FirstProducer": { + "carrierTechnologyParameters" : { + "carrierTechnology" : "FILE", + "parameters" : { + "standardIO" : true + } + }, + "eventProtocolParameters" : { + "eventProtocol" : "JSON" + } + } + }, + "eventInputParameters": { + "FirstConsumer": { + "carrierTechnologyParameters" : { + "carrierTechnology" : "FILE", + "parameters" : { + "standardIO" : true + } + }, + "eventProtocolParameters" : { + "eventProtocol" : "JSON" + } + } + } + } + + .. container:: paragraph + + To test the policy try paste the following events into + the console as the APEX engine executes. Note, all tests + from Step 1 will still work perfectly since none of those + events originate from a branch with ``branch_ID`` between + ``1000`` and ``2000``. The 'Task Selection Logic' will + therefore pick the ``MorningBoozeCheck`` task as + expected, and will therefore give the same results. + + .. table:: Table 14. Inputs and Outputs when testing *My First Policy* + + +----------------------------------------------+------------------------------------------------------------+---------------------------+ + | Input Event (JSON) | Output Event (JSON) | comment | + +==============================================+============================================================+===========================+ + | .. container:: | .. container:: | Request to buy | + | | | alcohol item | + | .. container:: listingblock | .. container:: listingblock | (``item_ID=1249``) | + | | | | + | | | at *08:41:06 | + | | .. container:: content | GMT* on *Monday, | + | .. container:: content | | 02 January | + | | .. code:: | 2017*. | + | | | | + | | { | Sale is not | + | .. code:: | "nameSpace": "com.hyperm", | authorized. Uses | + | | "name": "SALE_AUTH", | the | + | | "version": "0.0.1", | ``MorningBoozeCheck`` | + | { | "source": "", | | + | "nameSpace": "com.hyperm", | "target": "", | task. | + | "name": "SALE_INPUT", | "amount": 1249, | | + | "version": "0.0.1", | "assistant_ID":12, | Note this test | + | "time": 1483346466000, | "authorised": false, | is copied from | + | "sale_ID": 99999992, | "branch_ID": 2, | Step 1 above, | + | "amount": 1249, | "item_ID": 1012, | and demonstrates | + | "item_ID": 1012, | "message": "Sale not authorised by policy ta | that the | + | "quantity": 1, | sk MorningBoozeCheck for time 08:41:06 GMT.| original | + | "assistant_ID": 12, | Alcohol can not be sold between 00:00:00 | ``MorningBoozeCheck`` | + | "branch_ID": 2 | GMT and 11:30:00 GMT", | | + | } | "notes": null, | task is | + | | "quantity": 1, | executed. | + | | "sale_ID": 99999992, | | + | | "time": 1483346466000 | | + | | } | | + +----------------------------------------------+------------------------------------------------------------+---------------------------+ + | .. container:: | .. container:: | Request to buy | + | | | alcohol | + | .. container:: listingblock | .. container:: listingblock | (``item_ID=1047``) | + | | | | + | | | at *10:14:33* on | + | | .. container:: content | *Thursday, 22 | + | .. container:: content | | December 2016*. | + | | .. code:: | | + | | | Sale is not | + | | { | authorized. Uses | + | .. code:: | "nameSpace" : "com.hyperm", | the | + | | "name" : "SALE_AUTH", | ``MorningBoozeCheckAlt1`` | + | | "version" : "0.0.1", | task. | + | { | "source" : "", | | + | | "target" : "", | | + | "nameSpace": "com.hyperm", | "sale_ID" : 99999981, | | + | "name": "SALE_INPUT", | "amount" : 299, | | + | "version": "0.0.1", | "assistant_ID": 1212, | | + | "time": 1482398073000, | "notes" : null, | | + | "sale_ID": 99999981, | "quantity" : 1, | | + | "amount": 299, | "branch_ID" : 1002, | | + | "item_ID": 1047, | "item_ID" : 1047, | | + | "quantity": 1, | "authorised" : false, | | + | "assistant_ID": 1212, | "time" : 1482398073000, | | + | "branch_ID": 1002 | "message" : "Sale not authorised by policy t | | + | } | ask MorningBoozeCheckAlt1 fortime | | + | | 10:14:33 CET. Alcohol can not be sold | | + | | between 00:00:00 CET and 13:00:00 CET or on | | + | | Sunday" | | + | | } | | + +----------------------------------------------+------------------------------------------------------------+---------------------------+ + | .. container:: | .. container:: | Request to buy | + | | | alcohol | + | .. container:: listingblock | .. container:: listingblock | (``item_ID=1443``) | + | | | | + | | | at *17:19:37* on | + | | .. container:: content | *Sunday, 18 | + | .. container:: content | | December 2016*. | + | | .. code:: | | + | | | Sale is not | + | | { | authorized. Uses | + | .. code:: | "nameSpace" : "com.hyperm", | the | + | | | ``MorningBoozeCheckAlt1`` | + | | "name" : "SALE_AUTH", | task. | + | { | | | + | "nameSpace": "com.hyperm", | "version" : "0.0.1", | | + | "name": "SALE_INPUT", | "source" : "", | | + | "version": "0.0.1", | "target" : "", | | + | "time": 1482077977000, | "sale_ID" : 99999982, | | + | "sale_ID": 99999982, | "amount" : 2199, | | + | "amount": 2199, | "assistant_ID" : 94, | | + | "item_ID": 1443, | "notes" : "Buy 3, get 1 free!!", | | + | "quantity": 12, | "quantity" : 12, | | + | "assistant_ID": 94, | "branch_ID" : 1003, | | + | "branch_ID": 1003, | "item_ID" : 1443, | | + | "notes": "Buy 3, get 1 free!!" | "authorised" : false, | | + | } | "time" : 1482077977000, | | + | | "message" : "Sale not authorised by policy t | | + | | ask MorningBoozeCheckAlt1 for | | + | | time 17:19:37 CET. Alcohol c | | + | | an not be sold between 00:00: | | + | | 00 CET and 13:00:00 CET or on | | + | | Sunday" | | + +----------------------------------------------+------------------------------------------------------------+---------------------------+ + | .. container:: | .. container:: | Request to buy | + | | | non-alcoholic | + | .. container:: listingblock | .. container:: listingblock | item | + | | | (``item_ID=5321``) | + | | | | + | | .. container:: content | at *11:13:09* on | + | .. container:: content | | *Monday, 2 | + | | .. code:: | January 2017*. | + | | | | + | | { | Sale is | + | .. code:: | "nameSpace" : "com.hyperm", | authorized. Uses | + | | "name" : "SALE_AUTH", | the | + | { | "version" : "0.0.1", | ``MorningBoozeCheckAlt1`` | + | "nameSpace": "com.hyperm", | "source" : "", | task. | + | "name": "SALE_INPUT", | "target" : "", | | + | "version": "0.0.1", | "sale_ID" : 99999983, | | + | "time": 1483351989000, | "amount" : 699, | | + | "sale_ID": 99999983, | "assistant_ID" : 2323, | | + | "amount": 699, | "notes" : "", | | + | "item_ID": 5321, | "quantity" : 1, | | + | "quantity": 1, | "branch_ID" : 1001, | | + | "assistant_ID": 2323, | "item_ID" : 5321, | | + | "branch_ID": 1001, | "authorised" : true, | | + | "notes": "" | "time" : 1483351989000, | | + | } | "message" : "Sale authorised by policy task | | + | | MorningBoozeCheckAlt1 for time 11:13:09 CET"| | + | | } | | + +----------------------------------------------+------------------------------------------------------------+---------------------------+ + +Policy 2 in CLI Editor +###################### + + .. container:: paragraph + + An equivalent version of the ``MyFirstPolicyModel`` + policy model can again be generated using the APEX CLI + editor. A sample APEX CLI script is shown below: + + .. container:: listingblock + + .. container:: title + + APEX CLI Editor code for Policy 2 + + .. container:: content + + .. code:: + + #------------------------------------------------------------------------------- + # ============LICENSE_START======================================================= + # Copyright (C) 2016-2018 Ericsson. All rights reserved. + # ================================================================================ + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + # + # SPDX-License-Identifier: Apache-2.0 + # ============LICENSE_END========================================================= + #------------------------------------------------------------------------------- + + model create name=MyFirstPolicyModel version=0.0.1 uuid=540226fb-55ee-4f0e-a444-983a0494818e description="This is my first Apex Policy Model." + + schema create name=assistant_ID_type version=0.0.1 uuid=36df4c71-9616-4206-8b53-976a5cd4bd87 description="A type for 'assistant_ID' values" flavour=Java schema=java.lang.Long + + schema create name=authorised_type version=0.0.1 uuid=d48b619e-d00d-4008-b884-02d76ea4350b description="A type for 'authorised' values" flavour=Java schema=java.lang.Boolean + + schema create name=branch_ID_type version=0.0.1 uuid=6468845f-4122-4128-8e49-0f52c26078b5 description="A type for 'branch_ID' values" flavour=Java schema=java.lang.Long + + schema create name=item_ID_type version=0.0.1 uuid=4f227ff1-aee0-453a-b6b6-9a4b2e0da932 description="A type for 'item_ID' values" flavour=Java schema=java.lang.Long + + schema create name=message_type version=0.0.1 uuid=ad1431bb-3155-4e73-b5a3-b89bee498749 description="A type for 'message' values" flavour=Java schema=java.lang.String + + schema create name=notes_type version=0.0.1 uuid=eecfde90-896c-4343-8f9c-2603ced94e2d description="A type for 'notes' values" flavour=Java schema=java.lang.String + + schema create name=price_type version=0.0.1 uuid=52c2fc45-fd8c-463c-bd6f-d91b0554aea7 description="A type for 'amount'/'price' values" flavour=Java schema=java.lang.Long + + schema create name=quantity_type version=0.0.1 uuid=ac3d9842-80af-4a98-951c-bd79a431c613 description="A type for 'quantity' values" flavour=Java schema=java.lang.Integer + + schema create name=sale_ID_type version=0.0.1 uuid=cca47d74-7754-4a61-b163-ca31f66b157b description="A type for 'sale_ID' values" flavour=Java schema=java.lang.Long + + schema create name=timestamp_type version=0.0.1 uuid=fd594e88-411d-4a94-b2be-697b3a0d7adf description="A type for 'time' values" flavour=Java schema=java.lang.Long + + task create name=MorningBoozeCheck version=0.0.1 uuid=3351b0f4-cf06-4fa2-8823-edf67bd30223 description=LS + This task checks if the sales request is for an item that contains alcohol. + If the local time is between 00:00:00 and 11:30:00 then the sale is not authorised. Otherwise the sale is authorised. + In this implementation we assume that all items with item_ID values between 1000 and 2000 contain alcohol :-) + LE + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=sale_ID schemaName=sale_ID_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=amount schemaName=price_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=assistant_ID schemaName=assistant_ID_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=notes schemaName=notes_type schemaVersion=0.0.1 optional=true + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=quantity schemaName=quantity_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=branch_ID schemaName=branch_ID_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=item_ID schemaName=item_ID_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheck version=0.0.1 fieldName=time schemaName=timestamp_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=sale_ID schemaName=sale_ID_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=amount schemaName=price_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=assistant_ID schemaName=assistant_ID_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=notes schemaName=notes_type schemaVersion=0.0.1 optional=true + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=quantity schemaName=quantity_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=branch_ID schemaName=branch_ID_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=item_ID schemaName=item_ID_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=authorised schemaName=authorised_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=time schemaName=timestamp_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheck version=0.0.1 fieldName=message schemaName=message_type schemaVersion=0.0.1 optional=true + task logic create name=MorningBoozeCheck version=0.0.1 logicFlavour=MVEL logic=LS + /* + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + import java.util.Date; + import java.util.Calendar; + import java.util.TimeZone; + import java.text.SimpleDateFormat; + + logger.info("Task Execution: '"+subject.id+"'. Input Fields: '"+inFields+"'"); + + outFields.put("amount" , inFields.get("amount")); + outFields.put("assistant_ID", inFields.get("assistant_ID")); + outFields.put("notes" , inFields.get("notes")); + outFields.put("quantity" , inFields.get("quantity")); + outFields.put("branch_ID" , inFields.get("branch_ID")); + outFields.put("item_ID" , inFields.get("item_ID")); + outFields.put("time" , inFields.get("time")); + outFields.put("sale_ID" , inFields.get("sale_ID")); + + item_id = inFields.get("item_ID"); + + //The events used later to test this task use GMT timezone! + gmt = TimeZone.getTimeZone("GMT"); + timenow = Calendar.getInstance(gmt); + df = new SimpleDateFormat("HH:mm:ss z"); + df.setTimeZone(gmt); + timenow.setTimeInMillis(inFields.get("time")); + + midnight = timenow.clone(); + midnight.set( + timenow.get(Calendar.YEAR),timenow.get(Calendar.MONTH), + timenow.get(Calendar.DATE),0,0,0); + eleven30 = timenow.clone(); + eleven30.set( + timenow.get(Calendar.YEAR),timenow.get(Calendar.MONTH), + timenow.get(Calendar.DATE),11,30,0); + + itemisalcohol = false; + if(item_id != null && item_id >=1000 && item_id < 2000) + itemisalcohol = true; + + if( itemisalcohol + && timenow.after(midnight) && timenow.before(eleven30)){ + outFields.put("authorised", false); + outFields.put("message", "Sale not authorised by policy task "+subject.taskName+ + " for time "+df.format(timenow.getTime())+ + ". Alcohol can not be sold between "+df.format(midnight.getTime())+ + " and "+df.format(eleven30.getTime())); + return true; + } + else{ + outFields.put("authorised", true); + outFields.put("message", "Sale authorised by policy task "+subject.taskName+ + " for time "+df.format(timenow.getTime())); + return true; + } + + /* + This task checks if a sale request is for an item that is an alcoholic drink. + If the local time is between 00:00:00 GMT and 11:30:00 GMT then the sale is not + authorised. Otherwise the sale is authorised. + In this implementation we assume that items with item_ID value between 1000 and + 2000 are all alcoholic drinks :-) + */ + LE + + task create name=MorningBoozeCheckAlt1 version=0.0.1 uuid=bc6d90c9-c902-4686-afd3-925b30e39990 description=LS + This task checks if a sale request is for an item that is an alcoholic drink. + If the local time is between 00:00:00 CET and 13:00:00 CET then the sale is not authorised. + Also alcohol sales are not allowed on Sundays. Otherwise the sale is authorised. + In this implementation we assume that items with item_ID between 1000 and 2000 are all alcoholic drinks + LE + task inputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=sale_ID schemaName=sale_ID_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=amount schemaName=price_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=assistant_ID schemaName=assistant_ID_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=notes schemaName=notes_type schemaVersion=0.0.1 optional=true + task inputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=quantity schemaName=quantity_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=branch_ID schemaName=branch_ID_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=item_ID schemaName=item_ID_type schemaVersion=0.0.1 + task inputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=time schemaName=timestamp_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=sale_ID schemaName=sale_ID_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=amount schemaName=price_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=assistant_ID schemaName=assistant_ID_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=notes schemaName=notes_type schemaVersion=0.0.1 optional=true + task outputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=quantity schemaName=quantity_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=branch_ID schemaName=branch_ID_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=item_ID schemaName=item_ID_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=authorised schemaName=authorised_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=time schemaName=timestamp_type schemaVersion=0.0.1 + task outputfield create name=MorningBoozeCheckAlt1 version=0.0.1 fieldName=message schemaName=message_type schemaVersion=0.0.1 optional=true + task logic create name=MorningBoozeCheckAlt1 version=0.0.1 logicFlavour=MVEL logic=LS + /* + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + import java.util.Date; + import java.util.Calendar; + import java.util.TimeZone; + import java.text.SimpleDateFormat; + + logger.info("Task Execution: '"+subject.id+"'. Input Event: '"+inFields+"'"); + + outFields.put("amount" , inFields.get("amount")); + outFields.put("assistant_ID", inFields.get("assistant_ID")); + outFields.put("notes" , inFields.get("notes")); + outFields.put("quantity" , inFields.get("quantity")); + outFields.put("branch_ID" , inFields.get("branch_ID")); + outFields.put("item_ID" , inFields.get("item_ID")); + outFields.put("time" , inFields.get("time")); + outFields.put("sale_ID" , inFields.get("sale_ID")); + + item_id = inFields.get("item_ID"); + + //The events used later to test this task use CET timezone! + cet = TimeZone.getTimeZone("CET"); + timenow = Calendar.getInstance(cet); + df = new SimpleDateFormat("HH:mm:ss z"); + df.setTimeZone(cet); + timenow.setTimeInMillis(inFields.get("time")); + + midnight = timenow.clone(); + midnight.set( + timenow.get(Calendar.YEAR),timenow.get(Calendar.MONTH), + timenow.get(Calendar.DATE),0,0,0); + onepm = timenow.clone(); + onepm.set( + timenow.get(Calendar.YEAR),timenow.get(Calendar.MONTH), + timenow.get(Calendar.DATE),13,0,0); + + itemisalcohol = false; + if(item_id != null && item_id >=1000 && item_id < 2000) + itemisalcohol = true; + + if( itemisalcohol && + ( (timenow.after(midnight) && timenow.before(onepm)) + || + (timenow.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) + )){ + outFields.put("authorised", false); + outFields.put("message", "Sale not authorised by policy task "+subject.taskName+ + " for time "+df.format(timenow.getTime())+ + ". Alcohol can not be sold between "+df.format(midnight.getTime())+ + " and "+df.format(onepm.getTime()) +" or on Sunday"); + return true; + } + else{ + outFields.put("authorised", true); + outFields.put("message", "Sale authorised by policy task "+subject.taskName+ + " for time "+df.format(timenow.getTime())); + return true; + } + + /* + This task checks if a sale request is for an item that is an alcoholic drink. + If the local time is between 00:00:00 CET and 13:00:00 CET then the sale is not authorised. + Also alcohol sales are not allowed on Sundays. Otherwise the sale is authorised. + In this implementation we assume that items with item_ID between 1000 and 2000 are all alcoholic drinks :-) + */ + LE + + event create name=SALE_AUTH version=0.0.1 uuid=c4500941-3f98-4080-a9cc-5b9753ed050b description="An event emitted by the Policy to indicate whether the sale of an item has been authorised" nameSpace=com.hyperm source="APEX" target="POS" + event parameter create name=SALE_AUTH version=0.0.1 parName=amount schemaName=price_type schemaVersion=0.0.1 + event parameter create name=SALE_AUTH version=0.0.1 parName=assistant_ID schemaName=assistant_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_AUTH version=0.0.1 parName=authorised schemaName=authorised_type schemaVersion=0.0.1 + event parameter create name=SALE_AUTH version=0.0.1 parName=branch_ID schemaName=branch_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_AUTH version=0.0.1 parName=item_ID schemaName=item_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_AUTH version=0.0.1 parName=message schemaName=message_type schemaVersion=0.0.1 optional=true + event parameter create name=SALE_AUTH version=0.0.1 parName=notes schemaName=notes_type schemaVersion=0.0.1 optional=true + event parameter create name=SALE_AUTH version=0.0.1 parName=quantity schemaName=quantity_type schemaVersion=0.0.1 + event parameter create name=SALE_AUTH version=0.0.1 parName=sale_ID schemaName=sale_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_AUTH version=0.0.1 parName=time schemaName=timestamp_type schemaVersion=0.0.1 + + event create name=SALE_INPUT version=0.0.1 uuid=4f04aa98-e917-4f4a-882a-c75ba5a99374 description="An event raised by the PoS system each time an item is scanned for purchase" nameSpace=com.hyperm source="POS" target="APEX" + event parameter create name=SALE_INPUT version=0.0.1 parName=amount schemaName=price_type schemaVersion=0.0.1 + event parameter create name=SALE_INPUT version=0.0.1 parName=assistant_ID schemaName=assistant_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_INPUT version=0.0.1 parName=branch_ID schemaName=branch_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_INPUT version=0.0.1 parName=item_ID schemaName=item_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_INPUT version=0.0.1 parName=notes schemaName=notes_type schemaVersion=0.0.1 optional=true + event parameter create name=SALE_INPUT version=0.0.1 parName=quantity schemaName=quantity_type schemaVersion=0.0.1 + event parameter create name=SALE_INPUT version=0.0.1 parName=sale_ID schemaName=sale_ID_type schemaVersion=0.0.1 + event parameter create name=SALE_INPUT version=0.0.1 parName=time schemaName=timestamp_type schemaVersion=0.0.1 + + + policy create name=MyFirstPolicy version=0.0.1 uuid=6c5e410f-489a-46ff-964e-982ce6e8b6d0 description="This is my first Apex policy. It checks if a sale should be authorised or not." template=FREEFORM firstState=BoozeAuthDecide + policy state create name=MyFirstPolicy version=0.0.1 stateName=BoozeAuthDecide triggerName=SALE_INPUT triggerVersion=0.0.1 defaultTaskName=MorningBoozeCheck defaultTaskVersion=0.0.1 + policy state output create name=MyFirstPolicy version=0.0.1 stateName=BoozeAuthDecide outputName=MorningBoozeCheck_Output_Direct eventName=SALE_AUTH eventVersion=0.0.1 nextState=NULL + policy state taskref create name=MyFirstPolicy version=0.0.1 stateName=BoozeAuthDecide taskLocalName=MorningBoozeCheckAlt1 taskName=MorningBoozeCheckAlt1 taskVersion=0.0.1 outputType=DIRECT outputName=MorningBoozeCheck_Output_Direct + policy state taskref create name=MyFirstPolicy version=0.0.1 stateName=BoozeAuthDecide taskLocalName=MorningBoozeCheck taskName=MorningBoozeCheck taskVersion=0.0.1 outputType=DIRECT outputName=MorningBoozeCheck_Output_Direct + policy state selecttasklogic create name=MyFirstPolicy version=0.0.1 stateName=BoozeAuthDecide logicFlavour=JAVASCRIPT logic=LS + /* + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + + var returnValueType = Java.type("java.lang.Boolean"); + var returnValue = new returnValueType(true); + + executor.logger.info("Task Selection Execution: '"+executor.subject.id+"'. Input Event: '"+executor.inFields+"'"); + + branchid = executor.inFields.get("branch_ID"); + taskorig = executor.subject.getTaskKey("MorningBoozeCheck"); + taskalt = executor.subject.getTaskKey("MorningBoozeCheckAlt1"); + taskdef = executor.subject.getDefaultTaskKey(); + + if(branchid >=0 && branchid <1000){ + taskorig.copyTo(executor.selectedTask); + } + else if (branchid >=1000 && branchid <2000){ + taskalt.copyTo(executor.selectedTask); + } + else{ + taskdef.copyTo(executor.selectedTask); + } + + /* + This task selection logic selects task "MorningBoozeCheck" for branches with 0<=branch_ID<1000 and selects task "MorningBoozeCheckAlt1" for branches with 1000<=branch_ID<2000. Otherwise the default task is selected. In this case the default task is also "MorningBoozeCheck" + */ + LE + +APEX Logging +^^^^^^^^^^^^ + +Introduction to APEX Logging +---------------------------- + + .. container:: paragraph + + All APEX components make extensive use of logging using the + logging façade `SLF4J `__ with the + backend `Logback `__. Both are used + off-the-shelve, so the standard documentation and + configuration apply to APEX logging. For details on how to + work with logback please see the `logback + manual `__. + + .. container:: paragraph + + The APEX applications is the logback configuration file + ``$APEX_HOME/etc/logback.xml`` (Windows: + ``%APEX_HOME%\etc\logback.xml``). The logging backend is set + to no debug, i.e. logs from the logging framework should be + hidden at runtime. + + .. container:: paragraph + + The configurable log levels work as expected: + + .. container:: ulist + + - *error* (or *ERROR*) is used for serious errors in the + APEX runtime engine + + - *warn* (or *WARN*) is used for warnings, which in general + can be ignored but might indicate some deeper problems + + - *info* (or *INFO*) is used to provide generally + interesting messages for startup and policy execution + + - *debug* (or *DEBUG*) provides more details on startup and + policy execution + + - *trace* (or *TRACE*) gives full details on every aspect + of the APEX engine from start to end + + .. container:: paragraph + + The loggers can also be configured as expected. The standard + configuration (after installing APEX) uses log level *info* + on all APEX classes (components). + + .. container:: paragraph + + The applications and scripts in ``$APEX_HOME/bin`` (Windows: + ``%APEX_HOME\bin``) are configured to use the logback + configuration ``$APEX_HOME/etc/logback.xml`` (Windows: + ``%APEX_HOME\etc\logback.xml``). There are multiple ways to + use different logback configurations, for instance: + + .. container:: ulist + + - Maintain multiple configurations in ``etc``, for instance + a ``logback-debug.xml`` for deep debugging and a + ``logback-production.xml`` for APEX in production mode, + then copy the required configuration file to the used + ``logback.xml`` prior starting APEX + + - Edit the scripts in ``bin`` to use a different logback + configuration file (only recommended if you are familiar + with editing bash scripts or windows batch files) + +Standard Logging Configuration +------------------------------ + + .. container:: paragraph + + The standard logging configuration defines a context *APEX*, + which is used in the standard output pattern. The location + for log files is defined in the property ``VAR_LOG`` and set + to ``/var/log/onap/policy/apex-pdp``. The standard status + listener is set to *NOP* and the overall logback + configuration is set to no debug. + + .. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + + + + Apex + + + ...appenders + ...loggers + + +.. container:: paragraph + + The first appender defined is called ``STDOUT`` for logs to standard + out. + +.. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + + + %d %contextName [%t] %level %logger{36} - %msg%n + + + +.. container:: paragraph + + The root level logger then is set to the level *info* using the + standard out appender. + +.. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + + + + +.. container:: paragraph + + The second appender is called ``FILE``. It writes logs to a file + ``apex.log``. + +.. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + + ${VAR_LOG}/apex.log + + %d %-5relative [procId=${processId}] [%thread] %-5level %logger{26} - %msg %n %ex{full} + + + +.. container:: paragraph + + The third appender is called ``CTXT_FILE``. It writes logs to a file + ``apex_ctxt.log``. + +.. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + + ${VAR_LOG}/apex_ctxt.log + + %d %-5relative [procId=${processId}] [%thread] %-5level %logger{26} - %msg %n %ex{full} + + + +.. container:: paragraph + + The last definitions are for specific loggers. The first logger + captures all standard APEX classes. It is configured for log level + *info* and uses the standard output and file appenders. The second + logger captures APEX context classes responsible for context + monitoring. It is configured for log level *trace* and uses the + context file appender. + +.. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + + + + + + + + + + +Adding Logback Status and Debug +------------------------------- + + .. container:: paragraph + + To activate logback status messages change the status listener + from 'NOP' to for instance console. + + .. container:: listingblock + + .. container:: content + + .. code:: + + + + .. container:: paragraph + + To activate all logback debugging, for instance to debug a new + logback configuration, activate the debug attribute in the + configuration. + + .. container:: listingblock + + .. container:: content + + .. code:: + + + ... + + +Logging External Components +--------------------------- + + .. container:: paragraph + + Logback can also be configured to log any other, external + components APEX is using, if they are using the common logging + framework. + + .. container:: paragraph + + For instance, the context component of APEX is using *Infinispan* + and one can add a logger for this external component. The + following example adds a logger for *Infinispan* using the + standard output appender. + + .. container:: listingblock + + .. container:: content + + .. code:: + + + + + + .. container:: paragraph + + Another example is Apache Zookeeper. The following example adds a + logger for Zookeeper using the standard outout appender. + + .. container:: listingblock + + .. container:: content + + .. code:: + + + + + +Configuring loggers for Policy Logic +------------------------------------ + + .. container:: paragraph + + The logging for the logic inside a policy (task logic, task + selection logic, state finalizer logic) can be configured separate + from standard logging. The logger for policy logic is + ``org.onap.policy.apex.executionlogging``. The following example + defines + + .. container:: ulist + + - a new appender for standard out using a very simple pattern + (simply the actual message) + + - a logger for policy logic to standard out using the new + appender and the already described file appender. + + .. container:: listingblock + + .. container:: content + + .. code:: + + + + policy: %msg\n + + + + + + + + + .. container:: paragraph + + It is also possible to use specific logging for parts of policy + logic. The following example defines a logger for task logic. + + .. container:: listingblock + + .. container:: content + + .. code:: + + + + + +Rolling File Appenders +---------------------- + + .. container:: paragraph + + Rolling file appenders are a good option for more complex logging + of a production or complex testing APEX installation. The standard + logback configuration can be used for these use cases. This + section gives two examples for the standard logging and for + context logging. + + .. container:: paragraph + + First the standard logging. The following example defines a + rolling file appender. The appender rolls over on a daily basis. + It allows for a file size of 100 MB. + + .. container:: listingblock + + .. container:: content + + .. code:: + + + ${VAR_LOG}/apex.log + + + + ${VAR_LOG}/apex_%d{yyyy-MM-dd}.%i.log.gz + + 4 + + + 100MB + + + + + %d %-5relative [procId=${processId}] [%thread] %-5level %logger{26} - %msg %ex{full} %n + + + + + .. container:: paragraph + + A very similar configuration can be used for a rolling file + appender logging APEX context. + + .. container:: listingblock + + .. container:: content + + .. code:: + + + ${VAR_LOG}/apex_ctxt.log + + ${VAR_LOG}/apex_ctxt_%d{yyyy-MM-dd}.%i.log.gz + + 4 + + 100MB + + + + + %d %-5relative [procId=${processId}] [%thread] %-5level %logger{26} - %msg %ex{full} %n + + + + +Example Configuration for Logging Logic +--------------------------------------- + + .. container:: paragraph + + The following example shows a configuration that logs policy logic + to standard out and a file (*info*). All other APEX components are + logging to a file (*debug*).. This configuration an be used in a + pre-production phase with the APEX engine still running in a + separate terminal to monitor policy execution. This logback + configuration is in the APEX installation as + ``etc/logback-logic.xml``. + + .. container:: listingblock + + .. container:: content + + .. code:: + + + + + Apex + + + + + %d %contextName [%t] %level %logger{36} - %msg%n + + + + + ${VAR_LOG}/apex.log + + + %d %-5relative [procId=${processId}] [%thread] %-5level%logger{26} - %msg %n %ex{full} + + + + + + + policy: %msg\n + + + + + + + + + + + + + + + + + +Example Configuration for a Production Server +--------------------------------------------- + + .. container:: paragraph + + The following example shows a configuration that logs all APEX + components, including policy logic, to a file (*debug*). This + configuration an be used in a production phase with the APEX + engine being executed as a service on a system without console + output. This logback configuration is in the APEX installation as + ``logback-server.xml`` + + .. container:: listingblock + + .. container:: content + + .. code:: + + + + + Apex + + + + ${VAR_LOG}/apex.log + + + %d %-5relative [procId=${processId}] [%thread] %-5level%logger{26} - %msg %n %ex{full} + + + + + + + + + + + + + +Building a System with Websocket Backend +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Websockets +---------- + + .. container:: paragraph + + Websocket is a protocol to run sockets of HTTP. Since it in + essence a socket, the connection is realized between a + server (waiting for connections) and a client (connecting to + a server). Server/client separation is only important for + connection establishment, once connected, everyone can + send/receive on the same socket (as any standard socket + would allow). + + .. container:: paragraph + + Standard Websocket implementations are simple, no + publish/subscribe and no special event handling. Most + servers simply send all incoming messages to all + connections. There is a PubSub definition on top of + Websocket called `WAMP `__. APEX + does not support WAMP at the moment. + +Websocket in Java +----------------- + + .. container:: paragraph + + In Java, `JSR + 356 `__ + defines the standard Websocket API. This JSR is part of Jave + EE 7 standard. For Java SE, several implementations exist in + open source. Since Websockets are a stable standard and + simple, most implementations are stable and ready to use. A + lot of products support Websockets, like Spring, JBoss, + Netty, … there are also Kafka extensions for Websockets. + +Websocket Example Code for Websocket clients (FOSS) +--------------------------------------------------- + + .. container:: paragraph + + There are a lot of implementations and examples available on + Github for Websocket clients. If one is using Java EE 7, + then one can also use the native Websocket implementation. + Good examples for clients using simply Java SE are here: + + .. container:: ulist + + - `Websocket + implementation `__ + + - `Websocket sending client example, using + AWT `__ + + - `Websocket receiving client example (simple echo + client) `__ + + .. container:: paragraph + + For Java EE, the native Websocket API is explained here: + + .. container:: ulist + + - `Oracle + docs `__ + + - link: `An + example `__ + +BCP: Websocket Configuration +---------------------------- + + .. container:: paragraph + + The probably best is to configure APEX for Websocket servers + for input (ingress, consume) and output (egress, produce) + interfaces. This means that APEX will start Websocket + servers on named ports and wait for clients to connect. + Advantage: once APEX is running all connectivity + infrastructure is running as well. Consequence: if APEX is + not running, everyone else is in the dark, too. + + .. container:: paragraph + + The best protocol to be used is JSON string. Each event on + any interface is then a string with a JSON encoding. JSON + string is a little bit slower than byte code, but we doubt + that this will be noticeable. A further advantage of JSON + strings over Websockets with APEX starting the servers: it + is very easy to connect web browsers to such a system. + Simple connect the web browser to the APEX sockets and + send/read JSON strings. + + .. container:: paragraph + + Once APEX is started you simply connect Websocket clients to + it, and send/receive event. When APEX is terminated, the + Websocket servers go down, and the clients will be + disconnected. APEX does not (yet) support auto-client + reconnect nor WAMP, so clients might need to be restarted or + reconnected manually after an APEX boot. + +Demo with VPN Policy Model +-------------------------- + + .. container:: paragraph + + We assume that you have an APEX installation using the full + package, i.e. APEX with all examples, of version ``0.5.6`` + or higher. We will use the VPN policy from the APEX examples + here. + + .. container:: paragraph + + Now, have the following ready to start the demo: + + .. container:: ulist + + - 3 terminals on the host where APEX is running (we need 1 + for APEX and 1 for each client) + + - the events in the file + ``$APEX_HOME/examples/events/VPN/SetupEvents.json`` open + in an editor (we need to send those events to APEX) + + - the events in the file + ``$APEX_HOME/examples/events/VPN/Link09Events.json`` open + in an editor (we need to send those events to APEX) + +A Websocket Configuration for the VPN Domain +############################################ + + .. container:: paragraph + + Create a new APEX configuration using the VPN policy + model and configuring APEX as discussed above for + Websockets. Copy the following configuration into + ``$APEX_HOME/examples/config/VPN/Ws2WsServerAvroContextJsonEvent.json`` + (for Windows use + ``%APEX_HOME%\examples\config\VPN\Ws2WsServerAvroContextJsonEvent.json``): + + .. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + { + "engineServiceParameters" : { + "name" : "VPNApexEngine", + "version" : "0.0.1", + "id" : 45, + "instanceCount" : 1, + "deploymentPort" : 12345, + "policyModelFileName" : "examples/models/VPN/VPNPolicyModelAvro.json", + "engineParameters" : { + "executorParameters" : { + "MVEL" : { + "parameterClassName" : "org.onap.policy.apex.plugins.executor.mvel.MVELExecutorParameters" + } + }, + "contextParameters" : { + "parameterClassName" : "org.onap.policy.apex.context.parameters.ContextParameters", + "schemaParameters":{ + "Avro":{ + "parameterClassName" : "org.onap.policy.apex.plugins.context.schema.avro.AvroSchemaHelperParameters" + } + } + } + } + }, + "producerCarrierTechnologyParameters" : { + "carrierTechnology" : "WEBSOCKET", + "parameterClassName" : "org.onap.policy.apex.plugins.event.carrier.websocket.WEBSOCKETCarrierTechnologyParameters", + "parameters" : { + "wsClient" : false, + "port" : 42452 + } + }, + "producerEventProtocolParameters" : { + "eventProtocol" : "JSON" + }, + "consumerCarrierTechnologyParameters" : { + "carrierTechnology" : "WEBSOCKET", + "parameterClassName" : "org.onap.policy.apex.plugins.event.carrier.websocket.WEBSOCKETCarrierTechnologyParameters", + "parameters" : { + "wsClient" : false, + "port" : 42450 + } + }, + "consumerEventProtocolParameters" : { + "eventProtocol" : "JSON" + } + } + +Start APEX Engine +################# + + .. container:: paragraph + + In a new terminal, start APEX with the new configuration for + Websocket-Server ingress/egress: + + .. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + #: $APEX_HOME/bin/apexEngine.sh -c $APEX_HOME/examples/config/VPN/Ws2WsServerAvroContextJsonEvent.json + +.. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + #: %APEX_HOME%\bin\apexEngine.bat -c %APEX_HOME%\examples\config\VPN\Ws2WsServerAvroContextJsonEvent.json + +.. container:: paragraph + + Wait for APEX to start, it takes a while to create all Websocket + servers (about 8 seconds on a standard laptop without cached + binaries). depending on your log messages, you will see no (some, a + lot) log messages. If APEX starts correctly, the last few messages + you should see are: + +.. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + 2017-07-28 13:17:20,834 Apex [main] INFO c.e.a.s.engine.runtime.EngineService - engine model VPNPolicyModelAvro:0.0.1 added to the engine-AxArtifactKey:(name=VPNApexEngine-0,version=0.0.1) + 2017-07-28 13:17:21,057 Apex [Apex-apex-engine-service-0:0] INFO c.e.a.s.engine.runtime.EngineService - Engine AxArtifactKey:(name=VPNApexEngine-0,version=0.0.1) processing ... + 2017-07-28 13:17:21,296 Apex [main] INFO c.e.a.s.e.r.impl.EngineServiceImpl - Added the action listener to the engine + Started Apex service + +.. container:: paragraph + + APEX is running in the new terminal and will produce output when the + policy is triggered/executed. + +Run the Websocket Echo Client +############################# + + .. container:: paragraph + + The echo client is included in an APEX full installation. To run + the client, open a new shell (Unix, Cygwin) or command prompt + (``cmd`` on Windows). Then use the APEX application launcher to + start the client. + + .. important:: + APEX engine needs to run first + The example assumes that an APEX engine configured for *produce* carrier technology Websocket and *JSON* event protocol is executed first. + + +---------------------------------------------------------+-----------------------------------------------------------+ + | Unix, Cygwin | Windows | + +=========================================================+===========================================================+ + | .. container:: | .. container:: | + | | | + | .. container:: listingblock | .. container:: listingblock | + | | | + | .. container:: content | .. container:: content | + | | | + | .. code:: | .. code:: | + | | | + | # $APEX_HOME/bin/apexApps.sh ws-echo [args] | > %APEX_HOME%\bin\apexApps.bat ws-echo [args] | + +---------------------------------------------------------+-----------------------------------------------------------+ + + .. container:: paragraph + + Use the following command line arguments for server and port of + the Websocket server. The port should be the same as configured in + the APEX engine. The server host should be the host on which the + APEX engine is running + + .. container:: ulist + + - ``-p`` defines the Websocket port to connect to (defaults to + ``8887``) + + - ``-s`` defines the host on which a Websocket server is running + (defaults to ``localhost``) + + .. container:: paragraph + + Let’s assume that there is an APEX engine running, configured for + produce Websocket carrier technology, as server, for port 42452, + with produce event protocol JSON,. If we start the console client + on the same host, we can omit the ``-s`` options. We start the + console client as: + + .. container:: listingblock + + .. container:: content + + .. code:: + + # $APEX_HOME/bin/apexApps.sh ws-echo -p 42452 (1) + > %APEX_HOME%\bin\apexApps.bat ws-echo -p 42452 (2) + + .. container:: colist arabic + + +-------+--------------------------------+ + | **1** | Start client on Unix or Cygwin | + +-------+--------------------------------+ + | **2** | Start client on Windows | + +-------+--------------------------------+ + + .. container:: paragraph + + Once started successfully, the client will produce the following + messages (assuming we used ``-p 42452`` and an APEX engine is + running on ``localhost`` with the same port: + + .. container:: listingblock + + .. container:: content + + .. code:: + + ws-simple-echo: starting simple event echo + --> server: localhost + --> port: 42452 + + Once started, the application will simply print out all received events to standard out. + Each received event will be prefixed by '---' and suffixed by '====' + + + ws-simple-echo: opened connection to APEX (Web Socket Protocol Handshake) + +Run the Websocket Console Client +################################ + + .. container:: paragraph + + The console client is included in an APEX full installation. To + run the client, open a new shell (Unix, Cygwin) or command prompt + (``cmd`` on Windows). Then use the APEX application launcher to + start the client. + + .. important:: + APEX engine needs to run first + The example assumes that an APEX engine configured for *consume* carrier technology Websocket and *JSON* event + protocol is executed first. + + +------------------------------------------------------------+--------------------------------------------------------------+ + | Unix, Cygwin | Windows | + +============================================================+==============================================================+ + | .. container:: | .. container:: | + | | | + | .. container:: listingblock | .. container:: listingblock | + | | | + | .. container:: content | .. container:: content | + | | | + | .. code:: | .. code:: | + | | | + | # $APEX_HOME/bin/apexApps.sh ws-console [args] | > %APEX_HOME%\bin\apexApps.bat ws-console [args] | + +------------------------------------------------------------+--------------------------------------------------------------+ + + .. container:: paragraph + + Use the following command line arguments for server and port of + the Websocket server. The port should be the same as configured in + the APEX engine. The server host should be the host on which the + APEX engine is running + + .. container:: ulist + + - ``-p`` defines the Websocket port to connect to (defaults to + ``8887``) + + - ``-s`` defines the host on which a Websocket server is running + (defaults to ``localhost``) + + .. container:: paragraph + + Let’s assume that there is an APEX engine running, configured for + consume Websocket carrier technology, as server, for port 42450, + with consume event protocol JSON,. If we start the console client + on the same host, we can omit the ``-s`` options. We start the + console client as: + + .. container:: listingblock + + .. container:: content + + .. code:: + + # $APEX_HOME/bin/apexApps.sh ws-console -p 42450 (1) + > %APEX_HOME%\bin\apexApps.sh ws-console -p 42450 (2) + + .. container:: colist arabic + + +-------+--------------------------------+ + | **1** | Start client on Unix or Cygwin | + +-------+--------------------------------+ + | **2** | Start client on Windows | + +-------+--------------------------------+ + + .. container:: paragraph + + Once started successfully, the client will produce the following + messages (assuming we used ``-p 42450`` and an APEX engine is + running on ``localhost`` with the same port: + + .. container:: listingblock + + .. container:: content + + .. code:: + + ws-simple-console: starting simple event console + --> server: localhost + --> port: 42450 + + - terminate the application typing 'exit' or using 'CTRL+C' + - events are created by a non-blank starting line and terminated by a blank line + + + ws-simple-console: opened connection to APEX (Web Socket Protocol Handshake) + +Send Events +########### + + .. container:: paragraph + + Now you have the full system up and running: + + .. container:: ulist + + - Terminal 1: APEX ready and loaded + + - Terminal 2: an echo client, printing received messages produced + by the VPN policy + + - Terminal 2: a console client, waiting for input on the console + (standard in) and sending text to APEX + + .. container:: paragraph + + We started the engine with the VPN policy example. So all the + events we are using now are located in files in the following + example directory: + + .. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + #: $APEX_HOME/examples/events/VPN + > %APEX_HOME%\examples\events\VPN + +.. container:: paragraph + + To sends events, simply copy the content of the event files into + Terminal 3 (the console client). It will read multi-line JSON text + and send the events. So copy the content of ``SetupEvents.json`` into + the client. APEX will trigger a policy and produce some output, the + echo client will also print some events created in the policy. In + Terminal 1 (APEX) you’ll see some status messages from the policy as: + +.. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + {Link=L09, LinkUp=true} + L09 true + outFields: {Link=L09, LinkUp=true} + {Link=L10, LinkUp=true} + L09 true + L10 true + outFields: {Link=L10, LinkUp=true} + {CustomerName=C, LinkList=L09 L10, SlaDT=300, YtdDT=300} + *** Customers *** + C 300 300 [L09, L10] + outFields: {CustomerName=C, LinkList=L09 L10, SlaDT=300, YtdDT=300} + {CustomerName=A, LinkList=L09 L10, SlaDT=300, YtdDT=50} + *** Customers *** + A 300 50 [L09, L10] + C 300 300 [L09, L10] + outFields: {CustomerName=A, LinkList=L09 L10, SlaDT=300, YtdDT=50} + {CustomerName=D, LinkList=L09 L10, SlaDT=300, YtdDT=400} + *** Customers *** + A 300 50 [L09, L10] + C 300 300 [L09, L10] + D 300 400 [L09, L10] + outFields: {CustomerName=D, LinkList=L09 L10, SlaDT=300, YtdDT=400} + {CustomerName=B, LinkList=L09 L10, SlaDT=300, YtdDT=299} + *** Customers *** + A 300 50 [L09, L10] + B 300 299 [L09, L10] + C 300 300 [L09, L10] + D 300 400 [L09, L10] + outFields: {CustomerName=B, LinkList=L09 L10, SlaDT=300, YtdDT=299} + +.. container:: paragraph + + In Terminal 2 (echo-client) you see the received events, the last two + should look like: + +.. container:: listingblock + + .. container:: content + + .. code:: + :number-lines: + + ws-simple-echo: received + --------------------------------- + { + "name": "VPNCustomerCtxtActEvent", + "version": "0.0.1", + "nameSpace": "org.onap.policy.apex.domains.vpn.events", + "source": "Source", + "target": "Target", + "CustomerName": "C", + "LinkList": "L09 L10", + "SlaDT": 300, + "YtdDT": 300 + } + ================================= + + ws-simple-echo: received + --------------------------------- + { + "name": "VPNCustomerCtxtActEvent", + "version": "0.0.1", + "nameSpace": "org.onap.policy.apex.domains.vpn.events", + "source": "Source", + "target": "Target", + "CustomerName": "D", + "LinkList": "L09 L10", + "SlaDT": 300, + "YtdDT": 400 + } + ================================= + +.. container:: paragraph + + Congratulations, you have triggered a policy in APEX using + Websockets, the policy did run through, created events, picked up by + the echo-client. + +.. container:: paragraph + + Now you can send the Link 09 and Link 10 events, they will trigger + the actual VPN policy and some calculations are made. Let’s take the + Link 09 events from ``Link09Events.json``, copy them all into + Terminal 3 (the console). APEX will run the policy (with some status + output), and the echo client will receive and print events. + +.. container:: paragraph + + To terminate the applications, simply press ``CTRL+C`` in Terminal 1 + (APEX). This will also terminate the echo-client in Terminal 2. Then + type ``exit`` in Terminal 3 (or ``CTRL+C``) to terminate the + console-client. + +.. container:: + :name: footer + + .. container:: + :name: footer-text + + 2.0.0-SNAPSHOT + Last updated 2018-09-10 15:38:16 IST + +.. |Extract the TAR archive| image:: images/install-guide/win-extract-tar-gz.png +.. |Extract the APEX distribution| image:: images/install-guide/win-extract-tar.png +.. |REST Editor Start Screen| image:: images/install-guide/rest-start.png +.. |REST Editor with loaded SampleDomain Policy Model| image:: images/install-guide/rest-loaded.png +.. |APEX Configuration Matrix| image:: images/apex-intro/ApexEngineConfig.png +.. |File > New to create a new Policy Model| image:: images/mfp/MyFirstPolicy_P1_newPolicyModel1.png +.. |Create a new Policy Model| image:: images/mfp/MyFirstPolicy_P1_newPolicyModel2.png +.. |Right click to create a new event| image:: images/mfp/MyFirstPolicy_P1_newEvent1.png +.. |Fill in the necessary information for the 'SALE_INPUT' event and click 'Submit'| image:: images/mfp/MyFirstPolicy_P1_newEvent2.png +.. |Right click to create a new Item Schema| image:: images/mfp/MyFirstPolicy_P1_newItemSchema1.png +.. |Create a new Item Schema| image:: images/mfp/MyFirstPolicy_P1_newItemSchema2.png +.. |Add new event parameters to an event| image:: images/mfp/MyFirstPolicy_P1_newEvent3.png +.. |Right click to create a new task| image:: images/mfp/MyFirstPolicy_P1_newTask1.png +.. |Add input and out fields for the task| image:: images/mfp/MyFirstPolicy_P1_newTask2.png +.. |Add task logic the task| image:: images/mfp/MyFirstPolicy_P1_newTask3.png +.. |Create a new policy| image:: images/mfp/MyFirstPolicy_P1_newPolicy1.png +.. |Create a state| image:: images/mfp/MyFirstPolicy_P1_newState1.png +.. |Add a Task and Output Mapping| image:: images/mfp/MyFirstPolicy_P1_newState2.png +.. |Validate the policy model for error using the 'Model' > 'Validate' menu item| image:: images/mfp/MyFirstPolicy_P1_validatePolicyModel.png +.. |Download the completed policy model using the 'File' > 'Download' menu item| image:: images/mfp/MyFirstPolicy_P1_exportPolicyModel1.png +.. |Create a new alternative task MorningBoozeCheckAlt1| image:: images/mfp/MyFirstPolicy_P2_newTask1.png +.. |Right click to edit a policy| image:: images/mfp/MyFirstPolicy_P2_editPolicy1.png +.. |State definition with 2 Tasks and Task Selection Logic| image:: images/mfp/MyFirstPolicy_P2_editState1.png + -- cgit 1.2.3-korg