| .. 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 <https://tomcat.apache.org/>`__ |
| |
| - 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 <http://www.7-zip.org/>`__ |
| |
| - TAR and GZ to extract from that TAR.GZ |
| distribution |
| |
| .. container:: ulist |
| |
| - Windows for instance |
| `7Zip <http://www.7-zip.org/>`__ |
| |
| - 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 <https://kafka.apache.org/>`__ to |
| connect APEX to a Kafka message bus |
| |
| - `Hazelcast <https://hazelcast.com/>`__ to use |
| distributed hash maps for context |
| |
| - `Infinispan <http://infinispan.org/>`__ for |
| distributed context and persistence |
| |
| - `Docker <https://www.docker.com/>`__ 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 <https://git.onap.org/oparent/plain/settings.xml>`__. |
| |
| .. 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 <DIR> . | |
| | 03/09/2018 11:55 <DIR> .. | |
| | 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 <DIR> 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 <DIR> classes | |
| | 03/09/2018 11:54 <DIR> dependency-maven-plugin-markers | |
| | 03/09/2018 11:54 <DIR> etc | |
| | 03/09/2018 11:54 <DIR> examples | |
| | 03/09/2018 11:55 <DIR> install_hierarchy | |
| | 03/09/2018 11:54 <DIR> 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 <DIR> . | |
| | 03/09/2018 11:55 <DIR> .. | |
| | 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 <DIR> 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 <DIR> classes | |
| | 03/09/2018 11:54 <DIR> dependency-maven-plugin-markers | |
| | 03/09/2018 11:54 <DIR> etc | |
| | 03/09/2018 11:54 <DIR> examples | |
| | 03/09/2018 11:55 <DIR> install_hierarchy | |
| | 03/09/2018 11:54 <DIR> 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 ``<apex-dir>`` 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 <apex-dir> |
| |
| .. 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 |
| |
| ``<property name="VAR_LOG" value="/var/log/onap/policy/apex-pdp/" />`` |
| |
| .. container:: paragraph |
| |
| to |
| |
| .. container:: paragraph |
| |
| ``<property name="VAR_LOG" value="/PATH/TO/LOG/DIRECTORY/" />`` |
| |
| .. container:: paragraph |
| |
| On Windows, it is recommended to change the log directory to: |
| |
| .. container:: paragraph |
| |
| ``<property name="VAR_LOG" value="C:/apex/apex-full-2.0.0-SNAPSHOT/logs" />`` |
| |
| .. 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.<init>(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 <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 <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 <https://javaee.github.io/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 <https://tomcat.apache.org/>`__, however other web servers |
| can be used as well. |
| |
| .. container:: paragraph |
| |
| Install Apache Tomcat including the ``Manager App``, see `V9.0 |
| Docs <https://tomcat.apache.org/tomcat-9.0-doc/manager-howto.html#Configuring_Manager_Application_Access>`__ |
| 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 <https://tomcat.apache.org/tomcat-9.0-doc/index.html>`__ |
| or the `Manager App |
| HOW-TO <https://tomcat.apache.org/tomcat-9.0-doc/manager-howto.html>`__. |
| 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 <http://semver.org/>`_ | |
| | | _. | |
| | | 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 <https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html>`__ |
| (a |
| `tutorial <http://www.vogella.com/tutorials/JavaRegularExpressions/article.html>`__). |
| 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 <https://avro.apache.org/docs/1.8.1/spec.html#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 <https://kafka.apache.org/090/javadoc/org/apache/kafka/clients/consumer/KafkaConsumer.html>`__ |
| |
| - `Kafka Producer |
| Class <https://kafka.apache.org/090/javadoc/org/apache/kafka/clients/producer/KafkaProducer.html>`__ |
| |
| 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:: |
| |
| <servlet> |
| ... |
| <init-param> |
| ... |
| <param-value>org.onap.policy.apex.plugins.event.carrier.restserver</param-value> |
| </init-param> |
| ... |
| </servlet> |
| |
| 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 <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 <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 <MODEL_PROPS_FILE> name of the apex model properties file to use |
| -c,--command-file <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 <INPUT_MODEL_FILE> name of a file that contains an input model for the editor |
| -if,--ignore-failures <IGNORE_FAILURES_FLAG> true or false, ignore failures of commands in command files and continue |
| executing the command file |
| -l,--log-file <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 <CMD_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 <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 <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 <ADDRESS> the IP address to listen on. Default value is localhost to restrict access to the |
| local machine only. |
| -p,--port <PORT> port to use for the Apex RESTful editor REST calls. |
| -t,--time-to-live <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> port to use for the Apex Services REST calls |
| -t,--time-to-live <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> port to use for the Apex Services REST calls |
| -t,--time-to-live <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> port to use for the Apex Services REST calls |
| -t,--time-to-live <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] | [<application> [<application options>]] |
| |
| Options |
| -d <app> - 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 <name>`` 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 <MODEL-FILE> set the input policy model file |
| -t,--type <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 <MODEL-FILE> 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 <https://avro.apache.org/docs/current/spec.html>`__ |
| 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) <https://en.wikipedia.org/wiki/Directed_acyclic_graph>`__ |
| 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`` <https://en.wikipedia.org/wiki/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`` <https://en.wikipedia.org/wiki/JavaScript>`__, |
| ```MVEL`` <https://en.wikipedia.org/wiki/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) <https://en.wikipedia.org/wiki/Directed_acyclic_graph>`__ ) |
| 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`` <https://en.wikipedia.org/wiki/MVEL>`__ |
| scripting language. Sample task logic code (specified in |
| ```MVEL`` <https://en.wikipedia.org/wiki/MVEL>`__) is |
| given below. For a detailed guide to how to write your |
| own logic in |
| ```JavaScript`` <https://en.wikipedia.org/wiki/JavaScript>`__, |
| ```MVEL`` <https://en.wikipedia.org/wiki/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`` <https://en.wikipedia.org/wiki/JavaScript>`__ |
| scripting language. Sample task selection logic code |
| (specified in |
| ```JavaScript`` <https://en.wikipedia.org/wiki/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 <files/mfp-files/2/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" : "", | | |
| | tem_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 <https://www.slf4j.org/>`__ with the |
| backend `Logback <https://logback.qos.ch/>`__. 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 <https://logback.qos.ch/manual/index.html>`__. |
| |
| .. 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: |
| |
| <configuration debug="false"> |
| <statusListener class="ch.qos.logback.core.status.NopStatusListener" /> |
| |
| <contextName>Apex</contextName> |
| <property name="VAR_LOG" value="/var/log/onap/policy/apex-pdp/" /> |
| |
| ...appenders |
| ...loggers |
| </configuration> |
| |
| .. container:: paragraph |
| |
| The first appender defined is called ``STDOUT`` for logs to standard |
| out. |
| |
| .. container:: listingblock |
| |
| .. container:: content |
| |
| .. code:: |
| :number-lines: |
| |
| <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> |
| <encoder> |
| <Pattern>%d %contextName [%t] %level %logger{36} - %msg%n</Pattern> |
| </encoder> |
| </appender> |
| |
| .. 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: |
| |
| <root level="info"> |
| <appender-ref ref="STDOUT" /> |
| </root> |
| |
| .. container:: paragraph |
| |
| The second appender is called ``FILE``. It writes logs to a file |
| ``apex.log``. |
| |
| .. container:: listingblock |
| |
| .. container:: content |
| |
| .. code:: |
| :number-lines: |
| |
| <appender name="FILE" class="ch.qos.logback.core.FileAppender"> |
| <file>${VAR_LOG}/apex.log</file> |
| <encoder> |
| <pattern>%d %-5relative [procId=${processId}] [%thread] %-5level %logger{26} - %msg %n %ex{full}</pattern> |
| </encoder> |
| </appender> |
| |
| .. 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: |
| |
| <appender name="CTXT_FILE" class="ch.qos.logback.core.FileAppender"> |
| <file>${VAR_LOG}/apex_ctxt.log</file> |
| <encoder> |
| <pattern>%d %-5relative [procId=${processId}] [%thread] %-5level %logger{26} - %msg %n %ex{full}</pattern> |
| </encoder> |
| </appender> |
| |
| .. 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: |
| |
| |
| <logger name="org.onap.policy.apex" level="info" additivity="false"> |
| <appender-ref ref="STDOUT" /> |
| <appender-ref ref="FILE" /> |
| </logger> |
| |
| <logger name="org.onap.policy.apex.core.context.monitoring" level="TRACE" additivity="false"> |
| <appender-ref ref="CTXT_FILE" /> |
| </logger> |
| |
| 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:: |
| |
| <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" /> |
| |
| .. 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:: |
| |
| <configuration debug="true"> |
| ... |
| </configuration> |
| |
| 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:: |
| |
| <logger name="org.infinispan" level="INFO" additivity="false"> |
| <appender-ref ref="STDOUT" /> |
| </logger> |
| |
| .. 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:: |
| |
| <logger name="org.apache.zookeeper.ClientCnxn" level="INFO" additivity="false"> |
| <appender-ref ref="STDOUT" /> |
| </logger> |
| |
| 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:: |
| |
| <appender name="POLICY_APPENDER_STDOUT" class="ch.qos.logback.core.ConsoleAppender"> |
| <encoder> |
| <pattern>policy: %msg\n</pattern> |
| </encoder> |
| </appender> |
| |
| <logger name="org.onap.policy.apex.executionlogging" level="info" additivity="false"> |
| <appender-ref ref="POLICY_APPENDER_STDOUT" /> |
| <appender-ref ref="FILE" /> |
| </logger> |
| |
| .. 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:: |
| |
| <logger name="org.onap.policy.apex.executionlogging.TaskExecutionLogging" level="TRACE" additivity="false"> |
| <appender-ref ref="POLICY_APPENDER_STDOUT" /> |
| </logger> |
| |
| 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:: |
| |
| <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
| <file>${VAR_LOG}/apex.log</file> |
| <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| <!-- rollover daily --> |
| <!-- <fileNamePattern>xstream-%d{yyyy-MM-dd}.%i.txt</fileNamePattern> --> |
| <fileNamePattern>${VAR_LOG}/apex_%d{yyyy-MM-dd}.%i.log.gz |
| </fileNamePattern> |
| <maxHistory>4</maxHistory> |
| <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> |
| <!-- or whenever the file size reaches 100MB --> |
| <maxFileSize>100MB</maxFileSize> |
| </timeBasedFileNamingAndTriggeringPolicy> |
| </rollingPolicy> |
| <encoder> |
| <pattern> |
| %d %-5relative [procId=${processId}] [%thread] %-5level %logger{26} - %msg %ex{full} %n |
| </pattern> |
| </encoder> |
| </appender> |
| |
| .. container:: paragraph |
| |
| A very similar configuration can be used for a rolling file |
| appender logging APEX context. |
| |
| .. container:: listingblock |
| |
| .. container:: content |
| |
| .. code:: |
| |
| <appender name="CTXT-FILE" |
| class="ch.qos.logback.core.rolling.RollingFileAppender"> |
| <file>${VAR_LOG}/apex_ctxt.log</file> |
| <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> |
| <fileNamePattern>${VAR_LOG}/apex_ctxt_%d{yyyy-MM-dd}.%i.log.gz |
| </fileNamePattern> |
| <maxHistory>4</maxHistory> |
| <timeBasedFileNamingAndTriggeringPolicy |
| class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> |
| <maxFileSize>100MB</maxFileSize> |
| </timeBasedFileNamingAndTriggeringPolicy> |
| </rollingPolicy> |
| <encoder> |
| <pattern> |
| %d %-5relative [procId=${processId}] [%thread] %-5level %logger{26} - %msg %ex{full} %n |
| </pattern> |
| </encoder> |
| </appender> |
| |
| 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:: |
| |
| <configuration debug="false"> |
| <statusListener class="ch.qos.logback.core.status.NopStatusListener" /> |
| |
| <contextName>Apex</contextName> |
| <property name="VAR_LOG" value="/var/log/onap/policy/apex-pdp/" /> |
| |
| <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> |
| <encoder> |
| <Pattern>%d %contextName [%t] %level %logger{36} - %msg%n</Pattern> |
| </encoder> |
| </appender> |
| |
| <appender name="FILE" class="ch.qos.logback.core.FileAppender"> |
| <file>${VAR_LOG}/apex.log</file> |
| <encoder> |
| <pattern> |
| %d %-5relative [procId=${processId}] [%thread] %-5level%logger{26} - %msg %n %ex{full} |
| </pattern> |
| </encoder> |
| </appender> |
| |
| <appender name="POLICY_APPENDER_STDOUT" class="ch.qos.logback.core.ConsoleAppender"> |
| <encoder> |
| <pattern>policy: %msg\n</pattern> |
| </encoder> |
| </appender> |
| |
| <root level="error"> |
| <appender-ref ref="STDOUT" /> |
| </root> |
| |
| <logger name="org.onap.policy.apex" level="debug" additivity="false"> |
| <appender-ref ref="FILE" /> |
| </logger> |
| |
| <logger name="org.onap.policy.apex.executionlogging" level="info" additivity="false"> |
| <appender-ref ref="POLICY_APPENDER_STDOUT" /> |
| <appender-ref ref="FILE" /> |
| </logger> |
| </configuration> |
| |
| 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:: |
| |
| <configuration debug="false"> |
| <statusListener class="ch.qos.logback.core.status.NopStatusListener" /> |
| |
| <contextName>Apex</contextName> |
| <property name="VAR_LOG" value="/var/log/onap/policy/apex-pdp/" /> |
| |
| <appender name="FILE" class="ch.qos.logback.core.FileAppender"> |
| <file>${VAR_LOG}/apex.log</file> |
| <encoder> |
| <pattern> |
| %d %-5relative [procId=${processId}] [%thread] %-5level%logger{26} - %msg %n %ex{full} |
| </pattern> |
| </encoder> |
| </appender> |
| |
| <root level="debug"> |
| <appender-ref ref="FILE" /> |
| </root> |
| |
| <logger name="org.onap.policy.apex.executionlogging" level="debug" additivity="false"> |
| <appender-ref ref="FILE" /> |
| </logger> |
| </configuration> |
| |
| 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 <http://wamp-proto.org/>`__. APEX |
| does not support WAMP at the moment. |
| |
| Websocket in Java |
| ----------------- |
| |
| .. container:: paragraph |
| |
| In Java, `JSR |
| 356 <http://www.oracle.com/technetwork/articles/java/jsr356-1937161.html>`__ |
| 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 <https://github.com/TooTallNate/Java-WebSocket>`__ |
| |
| - `Websocket sending client example, using |
| AWT <https://github.com/TooTallNate/Java-WebSocket/blob/master/src/main/example/ChatClient.java>`__ |
| |
| - `Websocket receiving client example (simple echo |
| client) <https://github.com/TooTallNate/Java-WebSocket/blob/master/src/main/example/ExampleClient.java>`__ |
| |
| .. container:: paragraph |
| |
| For Java EE, the native Websocket API is explained here: |
| |
| .. container:: ulist |
| |
| - `Oracle |
| docs <http://www.oracle.com/technetwork/articles/java/jsr356-1937161.html>`__ |
| |
| - link: `An |
| example <http://www.programmingforliving.com/2013/08/jsr-356-java-api-for-websocket-client-api.html>`__ |
| |
| 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<enter>' 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<enter>`` 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 |
| |