- .. container:: paragraph
-
- The function of Task Selection Logic is to choose which task
- should be executed for an Apex State as one of the steps in an
- Apex Policy. Since each state must define a default task there is
- no need for Task Selection Logic unless the state uses more than
- one task. This logic can be specified in a number of ways,
- exploiting Apex’s plug-in architecture to support a range of logic
- executors. In Apex scripted Task Selection Logic can be written in
- any of these languages:
-
- .. container:: ulist
-
- - ```MVEL`` <https://en.wikipedia.org/wiki/MVEL>`__,
-
- - ```JavaScript`` <https://en.wikipedia.org/wiki/JavaScript>`__,
-
- - ```JRuby`` <https://en.wikipedia.org/wiki/JRuby>`__ or
-
- - ```Jython`` <https://en.wikipedia.org/wiki/Jython>`__.
-
- .. container:: paragraph
-
- These languages were chosen because the scripts can be compiled
- into Java bytecode at runtime and then efficiently executed
- natively in the JVM. Task Selection Logic an also be written
- directly in Java but needs to be compiled, with the resulting
- classes added to the classpath. There are also a number of other
- Task Selection Logic types but these are not supported as yet.
- This guide will focus on the scripted Task Selection Logic
- approaches, with MVEL and JavaScript being our favorite languages.
- In particular this guide will focus on the Apex aspects of the
- scripts. However, this guide does not attempt to teach you about
- the scripting languages themselves … that is up to you!
-
- .. tip::
- JVM-based scripting languages
- For more more information on Scripting for the Java platform see:
- https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/index.html
-
- .. note::
- What does Task Selection Logic do?
- When an Apex state references multiple tasks, there must be a way to dynamically decide
- which task should be chosen and executed. This can depend on the many factors, e.g. the
- *incoming event for the state*, *shared state* or *context*, *external context*,
- etc.. This is the function of a state’s Task Selection Logic. Obviously, if there is
- only one task then Task only one task then Task Selection Logic is not needed.
- Each state must also select one of the tasks a the *default state*. If the Task
- Selection Logic is unable to select an appropriate task, then it should select the
- *default task*. Once the task has been selected the Apex Engine will then execute that
- task.
-
- .. container:: paragraph
-
- First lets start with some simple Task Selection Logic, drawn from
- the "My First Apex Policy" example: The Task Selection Logic from
- the "My First Apex Policy" example is specified in JavaScript
- here:
-
- .. container:: listingblock
-
- .. container:: title
-
- Javascript code for the "My First Policy" Task Selection Logic
-
- .. container:: content
-
- .. code:: javascript
-
- /*
- * ============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:: paragraph
-
- The role of the Task Selection Logic in this simple example is to
- examine the value in one incoming field (``branchid``), then
- depending on that field’s value set the value for the selected
- task to the appropriate task (``MorningBoozeCheck``,
- ``MorningBoozeCheckAlt1``, or the default task).
-
- .. container:: paragraph
-
- Another thing to notice is that Task Selection Logic should return
- a ``java.lang.Boolean`` value ``true`` if the logic executed
- correctly. If the logic fails for some reason then ``false`` can
- be returned, but this will cause the policy invoking this task
- will fail and exit.
-
- .. note::
- How to return a value from Task Selection Logic
- Some languages explicitly support returning values from the script (e.g. MVEL and
- JRuby) using an explicit return statement (e.g. ``return true``), other languages do not (e.g.
- JavaScript and Jython). For languages that do not support the ``return`` statement, a special field called
- ``returnValue`` must be created to hold the result of the task logic operation (i.e. assign a ``java.lang.Boolean``
- value to the ``returnValue`` field before completing the task).
- Also, in MVEL if there is not explicit return statement then the return value of the last executed statement will
- return (e.g. the statement a=(1+2) will return the value 3).
-
- .. container:: paragraph
-
- Each of the scripting languages used in Apex can import and use
- standard Java libraries to perform complex tasks. Besides imported
- classes and normal language features Apex provides some natively
- available parameters and functions that can be used directly. At
- run-time these parameters are populated by the Apex execution
- environment and made natively available to logic scripts each time
- the logic script is invoked. (These can be accessed using the
- ``executor`` keyword for most languages, or can be accessed
- directly without the ``executor`` keyword in MVEL):
-
- Table 2. The ``executor`` Fields / Methods
- +-------------------------------------------------------+--------------------------------------------------------+
- | Unix, Cygwin | Windows |
- +=======================================================+========================================================+
- | .. container:: | .. container:: |
- | | |
- | .. container:: content | .. container:: content |
- | | |
- | .. code:: bash | .. code:: bash |
- | :number-lines: | :number-lines: |
- | | |
- | >c: | # cd /usr/local/src/apex-pdp |
- | >cd \dev\apex | # mvn clean install -DskipTests |
- | >mvn clean install -DskipTests | |
- +-------------------------------------------------------+--------------------------------------------------------+
-
-+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
-| Name | Type | Java type | Description |
-+============+=============+================================+=====================================================================================+
-| inFields | Fields | java.util.Map <String,Object> | .. container:: paragraph |
-| | | | |
-| | | | All fields in the state’s incoming event. This is implemented as a standard Java |
-| | | | Java (unmodifiable) Map |
-| | | | |
-| | | | .. container:: |
-| | | | |
-| | | | .. container:: content |
-| | | | |
-| | | | .. container:: paragraph |
-| | | | |
-| | | | **Example:** |
-| | | | |
-| | | | .. code:: javascript |
-| | | | |
-| | | | executor.logger.debug("Incoming fields: " |
-| | | | +executor.inFields.entrySet()); |
-| | | | var item_id = executor.incomingFields["item_ID"]; |
-| | | | if (item_id >=1000) { ... } |
-+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
-| outFields | Fields | java.util.Map <String,Object> | .. container:: paragraph |
-| | | | |
-| | | | The outgoing task fields. This is implemented as a standard initially empty Java |
-| | | | (modifiable) Map. To create a new schema-compliant instance of a field object |
-| | | | see the utility method subject.getOutFieldSchemaHelper() below |
-| | | | |
-| | | | .. container:: |
-| | | | |
-| | | | .. container:: content |
-| | | | |
-| | | | .. container:: paragraph |
-| | | | |
-| | | | **Example:** |
-| | | | |
-| | | | .. code:: javascript |
-| | | | |
-| | | | executor.outFields["authorised"] = false; |
-+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
-| logger | Logger | org.slf4j.ext.XLogger | .. container:: paragraph |
-| | | | |
-| | | | A helpful logger |
-| | | | |
-| | | | .. container:: |
-| | | | |
-| | | | .. container:: content |
-| | | | |
-| | | | .. container:: paragraph |
-| | | | |
-| | | | **Example:** |
-| | | | |
-| | | | .. code:: javascript |
-| | | | |
-| | | | executor.logger.info("Executing task: " |
-| | | | +executor.subject.id); |
-+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
-| TRUE/FALSE | boolean | java.lang.Boolean | .. container:: paragraph |
-| | | | |
-| | | | 2 helpful constants. These are useful to retrieve correct return values for the |
-| | | | task logic |
-| | | | |
-| | | | .. container:: |
-| | | | |
-| | | | .. container:: content |
-| | | | |
-| | | | .. container:: paragraph |
-| | | | |
-| | | | **Example:** |
-| | | | |
-| | | | .. code:: javascript |
-| | | | |
-| | | | var returnValue = executor.isTrue; |
-| | | | var returnValueType = Java.type("java.lang.Boolean"); |
-| | | | var returnValue = new returnValueType(true); |
-+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
-| subject | Task | TaskFacade | .. container:: paragraph |
-| | | | |
-| | | | This provides some useful information about the task that contains this task |
-| | | | logic. This object has some useful fields and methods : |
-| | | | |
-| | | | .. container:: ulist |
-| | | | |
-| | | | - **AxTask task** to get access to the full task definition of |
-| | | | the host task |
-| | | | |
-| | | | - **String getTaskName()** to get the name of the host task |
-| | | | |
-| | | | - **String getId()** to get the ID of the host task |
-| | | | |
-| | | | - **SchemaHelper getInFieldSchemaHelper( String fieldName )** to |
-| | | | get a ``SchemaHelper`` helper object to manipulate incoming |
-| | | | task fields in a schema-aware manner |
-| | | | |
-| | | | - **SchemaHelper getOutFieldSchemaHelper( String fieldName )** to |
-| | | | get a ``SchemaHelper`` helper object to manipulate outgoing |
-| | | | task fields in a schema-aware manner, e.g. to instantiate new |
-| | | | schema-compliant field objects to populate the |
-| | | | ``executor.outFields`` outgoing fields map |
-| | | | |
-| | | | .. container:: |
-| | | | |
-| | | | .. container:: content |
-| | | | |
-| | | | .. container:: paragraph |
-| | | | |
-| | | | **Example:** |
-| | | | |
-| | | | .. code:: javascript |
-| | | | |
-| | | | executor.logger.info("Task name: " |
-| | | | +executor.subject.getTaskName()); |
-| | | | executor.logger.info("Task id: " |
-| | | | +executor.subject.getId()); |
-| | | | executor.logger.info("Task inputs definitions: " |
-| | | | +"executor.subject.task.getInputFieldSet()); |
-| | | | executor.logger.info("Task outputs definitions: " |
-| | | | +"executor.subject.task.getOutputFieldSet()); |
-| | | | executor.outFields["authorised"] = executor.subject |
-| | | | .getOutFieldSchemaHelper("authorised") |
-| | | | .createNewInstance("false"); |
-+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
-| ContextAlbum getContextAlbum(String ctxtAlbumName ) | .. container:: paragraph |
-| | |
-| | A utility method to retrieve a ``ContextAlbum`` for use in the task. |
-| | This is how you access the context used by the task. The returned |
-| | ``ContextAlbum`` implements the ``java.util.Map <String,Object>`` |
-| | interface to get and set context as appropriate. The returned |
-| | ``ContextAlbum`` also has methods to lock context albums, get |
-| | information about the schema of the items to be stored in a context |
-| | album, and get a ``SchemaHelper`` to manipulate context album items. How |
-| | to define and use context in a task is described in the Apex |
-| | Programmer’s Guide and in the My First Apex Policy guide. |
-| | |
-| | .. container:: |
-| | |
-| | .. container:: content |
-| | |
-| | .. container:: paragraph |
-| | |
-| | **Example:** |
-| | |
-| | .. code:: javascript |
-| | |
-| | var bkey = executor.inFields.get("branch_ID"); |
-| | var cnts = executor.getContextMap("BranchCounts"); |
-| | cnts.lockForWriting(bkey); |
-| | cnts.put(bkey, cnts.get(bkey) + 1); |
-| | cnts.unlockForWriting(bkey); |
-+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
-
-Logic Cheatsheet
-----------------
-
- .. container:: paragraph
-
- Examples given here use Javascript (if not stated otherwise),
- other execution environments will be similar.
-
-Add Nashorn
-###########
-
- .. container:: paragraph
-
- First line in the logic use this import.
-
- .. container:: listingblock
-
- .. container:: title
-
- JS Nashorn
-
- .. container:: content
-
- .. code:: javascript
-
- load("nashorn:mozilla_compat.js");
+.. container:: paragraph
+
+ These languages were chosen because the scripts can be compiled into Java bytecode at runtime and then efficiently
+ executed natively in the JVM. Task Logic an also be written directly in Java but needs to be compiled, with the
+ resulting classes added to the classpath. There are also a number of other Task Logic types (e.g. Fuzzy Logic), but
+ these are not supported as yet. This guide will focus on the scripted Task Logic approaches, with MVEL and JavaScript
+ being our favorite languages. In particular this guide will focus on the Apex aspects of the scripts. However, this
+ guide does not attempt to teach you about the scripting languages themselves … that is up to you!
+
+.. tip::
+ JVM-based scripting languages For more more information on scripting for the Java platform see:
+ https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/index.html
+
+.. note::
+ What do Tasks do? The function of an Apex Task is to provide the logic that can be executed for an Apex State as one
+ of the steps in an Apex Policy. Each task receives some *incoming fields*, executes some logic (e.g: make a decision
+ based on *shared state* or *context*, *incoming fields*, *external context*, etc.), perhaps set some *shared state*
+ or *context* and then emits *outgoing fields* (in case of a single outgoing event), or a set of *outgoing fields*
+ (in case of multiple outgoing events). The state that uses the task is responsible for extracting the
+ *incoming fields* from the state input event. The state also has an *output mapper* associated with the task, and
+ this *output mapper* is responsible for mapping the *outgoing fields* from the task into an appropriate output event
+ for the state.
+
+.. container:: paragraph
+
+ First lets start with a sample task, drawn from the "My First Apex Policy" example: The task "MorningBoozeCheck"
+ from the "My First Apex Policy" example is available in both MVEL and JavaScript:
+
+.. container:: listingblock
+
+ .. container:: title
+
+ Javascript code for the ``MorningBoozeCheck`` task
+
+ .. container:: content
+
+ .. code:: javascript
+ :number-lines:
+
+ /*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+ 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 :-)
+ */
+
+ true;
+
+.. container:: listingblock
+
+ .. container:: title
+
+ MVEL code for the ``MorningBoozeCheck`` task
+
+ .. container:: content
+
+ .. code:: javascript
+ :number-lines:
+
+ /*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * 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:: paragraph
+
+ The role of the task in this simple example is to copy the values in the incoming fields into the outgoing
+ fields, then examine the values in some incoming fields (``item_id`` and ``time``), then set the values in some
+ other outgoing fields (``authorised`` and ``message``).
+
+.. container:: paragraph
+
+ Both MVEL and JavaScript like most JVM-based scripting languages can use standard Java libraries to perform
+ complex tasks. Towards the top of the scripts you will see how to import Java classes and packages to be used
+ directly in the logic. Another thing to notice is that Task Logic should return a ``java.lang.Boolean`` value
+ ``true`` if the logic executed correctly. If the logic fails for some reason then ``false`` can be returned, but
+ this will cause the policy invoking this task will fail and exit.
+
+.. note::
+ How to return a value from task logic
+ Some languages explicitly support returning values from the script (e.g. MVEL and JRuby) using an explicit
+ return statement (e.g. ``return true``), other languages do not (e.g. Jython). For
+ languages that do not support the ``return`` statement, a special field called ``returnValue`` must be
+ created to hold the result of the task logic operation (i.e. assign a ``java.lang.Boolean``
+ value to the ``returnValue`` field before completing the task).
+ Also, in MVEL if there is no explicit return statement then the return value of the last executed statement will
+ return (e.g. the statement a=(1+2) will return the value 3).
+
+ For Javascript, the last statement of a script must be a statement that evaluates to *true* or *false*, indicating
+ whether the script executed correctly or not. In the case where the script always executes to compeletion
+ sucessfully, simply add a last line with the statement *true'*. In cases where success or failure is assessed in the
+ script, create a boolean
+ local variable with a name such as ``returnvalue``. In the execution of the script, set ``returnValue`` to be ``true``
+ or ``false`` as appropriate. The last line of the scritp tehn should simply be ``returnValue;``, which returns the
+ value of ``returnValue``.
+
+.. container:: paragraph
+
+ Besides these imported classes and normal language features Apex provides some natively available parameters
+ and functions that can be used directly. At run-time these parameters are populated by the Apex execution
+ environment and made natively available to logic scripts each time the logic script is invoked. (These can be
+ accessed using the ``executor`` keyword for most languages, or can be accessed directly without the
+ ``executor`` keyword in MVEL):
+
+Table 1. The ``executor`` Fields / Methods
+
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | Name | Type | Java type | Description |
+ +=====================================================+==========================================================================+===============================+==================================================================================+
+ | inFields | Fields | java.util.Map <String,Object> |The incoming task fields, implemented as a standard Java (unmodifiable) Map |
+ | | | | |
+ | | | |**Example:** |
+ | | | | |
+ | | | |.. code:: javascript |
+ | | | | |
+ | | | | executor.logger.debug("Incoming fields: " +executor.inFields.entrySet()); |
+ | | | | var item_id = executor.incomingFields["item_ID"]; |
+ | | | | if (item_id >=1000) { ... } |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | outFields | Fields | java.util.Map <String,Object> |The outgoing task fields. This is implemented as a standard initially empty Java |
+ | | | |(modifiable) Map. To create a new schema-compliant instance of a field object |
+ | | | |see the utility method subject.getOutFieldSchemaHelper() below that takes the |
+ | | | |fieldName as an argument. |
+ | | | | |
+ | | | |**Example:** |
+ | | | | |
+ | | | |.. code:: javascript |
+ | | | | |
+ | | | | executor.outFields["authorised"] = false; |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | outFieldsList | Fields | java.util.Collection |The collection of outgoing task fields when there are multiple outputs from the |
+ | | | <Map<String, Object>> |final state. To create a new schema-compliant instance of a field, see the |
+ | | | |utility method subject.getOutFieldSchemaHelper() below that takes eventName and |
+ | | | |fieldName as arguments. |
+ | | | |To add the set of output fields to the outFieldsList, the utility method |
+ | | | |executor.addFieldsToOutput can be used as shown below. |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | void addFieldsToOutput(Map<String, Object> fields) |A utility method to add fields to outgoing fields. | | |
+ | |When there are multiple output events emitted from the task associated | | |
+ | |with a final state, this utility method can be used to add the | | |
+ | |corresponding fields to the outFieldsList. | | |
+ | | | | |
+ | |**Example:** | | |
+ | | | | |
+ | |.. code:: javascript | | |
+ | | | | |
+ | | var cdsRequestEventFields = java.util.HashMap(); | | |
+ | | var actionIdentifiers = executor.subject.getOutFieldSchemaHelper | | |
+ | | ("CDSRequestEvent","actionIdentifiers").createNewInstance(); | | |
+ | | cdsRequestEventFields.put("actionIdentifiers", actionIdentifiers); | | |
+ | | executor.addFieldsToOutput(cdsRequestEventFields); | | |
+ | | | | |
+ | | var logEventFields = java.util.HashMap(); | | |
+ | | logEventFields.put("status", "FINAL_SUCCESS"); | | |
+ | | executor.addFieldsToOutput(logEventFields); | | |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | logger | Logger | org.slf4j.ext.XLogger |A helpful logger |
+ | | | | |
+ | | | |**Example:** |
+ | | | | |
+ | | | |.. code:: javascript |
+ | | | | |
+ | | | | executor.logger.info("Executing task: " +executor.subject.id); |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | TRUE/FALSE | boolean | java.lang.Boolean |2 helpful constants. These are useful to retrieve correct return values for the |
+ | | | |task logic |
+ | | | | |
+ | | | |**Example:** |
+ | | | | |
+ | | | |.. code:: javascript |
+ | | | | |
+ | | | | var returnValue = executor.isTrue; |
+ | | | | var returnValueType = Java.type("java.lang.Boolean"); |
+ | | | | var returnValue = new returnValueType(true); |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | subject | Task | TaskFacade |This provides some useful information about the task that contains this task |
+ | | | |logic. This object has some useful fields and methods : |
+ | | | | |
+ | | | |.. container:: ulist |
+ | | | | |
+ | | | | - **AxTask task** to get access to the full task definition of the host task |
+ | | | | |
+ | | | | - **String getTaskName()** to get the name of the host task |
+ | | | | |
+ | | | | - **String getId()** to get the ID of the host task |
+ | | | | |
+ | | | | - **SchemaHelper getInFieldSchemaHelper( String fieldName )** to |
+ | | | | get a ``SchemaHelper`` helper object to manipulate incoming |
+ | | | | task fields in a schema-aware manner |
+ | | | | |
+ | | | | - **SchemaHelper getOutFieldSchemaHelper( String fieldName )** to |
+ | | | | get a ``SchemaHelper`` helper object to manipulate outgoing |
+ | | | | task fields in a schema-aware manner, e.g. to instantiate new |
+ | | | | schema-compliant field objects to populate the |
+ | | | | ``executor.outFields`` outgoing fields map. This can be used only when there |
+ | | | | is a single outgoing event from a task. |
+ | | | | |
+ | | | | - **SchemaHelper getOutFieldSchemaHelper( String eventname, String fieldName )**|
+ | | | | to get a ``SchemaHelper`` helper object to manipulate outgoing |
+ | | | | task fields in a schema-aware manner, e.g. to instantiate new |
+ | | | | schema-compliant field objects to populate the |
+ | | | | ``executor.outFieldsList`` collection of outgoing fields map. This must be |
+ | | | | used in case of multiple outgoing events from a task, as the intention is to |
+ | | | | fetch the schema of a field associated to one of the expected events. |
+ | | | | This method works fine in case of single outgoing event too, but the previous |
+ | | | | method is enough as the field anyway belongs to the single event. |
+ | | | | |
+ | | | |**Example:** |
+ | | | | |
+ | | | |.. code:: javascript |
+ | | | | |
+ | | | | executor.logger.info("Task name: " + executor.subject.getTaskName()); |
+ | | | | executor.logger.info("Task id: " + executor.subject.getId()); |
+ | | | | executor.outFields["authorised"] = executor.subject |
+ | | | | .getOutFieldSchemaHelper("authorised").createNewInstance("false"); |
+ | | | | |
+ | | | | var actionIdentifiers = executor.subject.getOutFieldSchemaHelper |
+ | | | | ("CDSRequestEvent","actionIdentifiers").createNewInstance(); |
+ | | | | actionIdentifiers.put("blueprintName", "sample-bp"); |
+ | | | | var cdsRequestEventFields = java.util.HashMap(); |
+ | | | | cdsRequestEventFields.put("actionIdentifiers", actionIdentifiers); |
+ | | | | executor.addFieldsToOutput(cdsRequestEventFields); |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | ContextAlbum getContextAlbum(String ctxtAlbumName ) |A utility method to retrieve a ``ContextAlbum`` for use in the task. | | |
+ | |This is how you access the context used by the task. The returned | | |
+ | |``ContextAlbum`` implements the ``java.util.Map <String,Object>`` | | |
+ | |interface to get and set context as appropriate. The returned | | |
+ | |``ContextAlbum`` also has methods to lock context albums, get | | |
+ | |information about the schema of the items to be stored in a context | | |
+ | |album, and get a ``SchemaHelper`` to manipulate context album items. How | | |
+ | |to define and use context in a task is described in the Apex | | |
+ | |Programmer’s Guide and in the My First Apex Policy guide. | | |
+ | | | | |
+ | |**Example:** | | |
+ | | | | |
+ | |.. code:: javascript | | |
+ | | | | |
+ | | var bkey = executor.inFields.get("branch_ID"); | | |
+ | | var cnts = executor.getContextMap("BranchCounts"); | | |
+ | | cnts.lockForWriting(bkey); | | |
+ | | cnts.put(bkey, cnts.get(bkey) + 1); | | |
+ | | cnts.unlockForWriting(bkey); | | |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+
+Writing APEX Task Selection Logic
+=================================
+
+.. container:: paragraph
+
+ The function of Task Selection Logic is to choose which task should be executed for an Apex State as one of
+ the steps in an Apex Policy. Since each state must define a default task there is no need for Task Selection
+ Logic unless the state uses more than one task. This logic can be specified in a number of ways, exploiting
+ Apex’s plug-in architecture to support a range of logic executors. In Apex scripted Task Selection Logic can be
+ written in any of these languages:
+
+.. container:: ulist
+
+ - ```MVEL`` <https://en.wikipedia.org/wiki/MVEL>`__,
+
+ - ```JavaScript`` <https://en.wikipedia.org/wiki/JavaScript>`__,
+
+ - ```JRuby`` <https://en.wikipedia.org/wiki/JRuby>`__ or
+
+ - ```Jython`` <https://en.wikipedia.org/wiki/Jython>`__.
+
+.. container:: paragraph
+
+ These languages were chosen because the scripts can be compiled into Java bytecode at runtime and then
+ efficiently executed natively in the JVM. Task Selection Logic an also be written directly in Java but needs to
+ be compiled, with the resulting classes added to the classpath. There are also a number of other Task Selection
+ Logic types but these are not supported as yet. This guide will focus on the scripted Task Selection Logic
+ approaches, with MVEL and JavaScript being our favorite languages. In particular this guide will focus on the
+ Apex aspects of the scripts. However, this guide does not attempt to teach you about the scripting languages
+ themselves … that is up to you!
+
+.. tip::
+ JVM-based scripting languages
+ For more more information on Scripting for the Java platform see:
+ https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/index.html
+
+.. note::
+ What does Task Selection Logic do?
+ When an Apex state references multiple tasks, there must be a way to dynamically decide
+ which task should be chosen and executed. This can depend on the many factors, e.g. the
+ *incoming event for the state*, *shared state* or *context*, *external context*,
+ etc.. This is the function of a state’s Task Selection Logic. Obviously, if there is
+ only one task then Task only one task then Task Selection Logic is not needed.
+ Each state must also select one of the tasks a the *default state*. If the Task
+ Selection Logic is unable to select an appropriate task, then it should select the
+ *default task*. Once the task has been selected the Apex Engine will then execute that task.
+
+.. container:: paragraph
+
+ First lets start with some simple Task Selection Logic, drawn from the "My First Apex Policy" example: The Task
+ Selection Logic from the "My First Apex Policy" example is specified in JavaScript here:
+
+.. container:: listingblock
+
+ .. container:: title
+
+ Javascript code for the "My First Policy" Task Selection Logic
+
+ .. container:: content
+
+ .. code:: javascript
+
+ /*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+ 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"
+ */
+
+ true;
+
+.. container:: paragraph
+
+ The role of the Task Selection Logic in this simple example is to examine the value in one incoming field
+ (``branchid``), then depending on that field’s value set the value for the selected task to the appropriate task
+ (``MorningBoozeCheck``, ``MorningBoozeCheckAlt1``, or the default task).
+
+.. container:: paragraph
+
+ Another thing to notice is that Task Selection Logic should return a ``java.lang.Boolean`` value ``true`` if
+ the logic executed correctly. If the logic fails for some reason then ``false`` can be returned, but this will
+ cause the policy invoking this task will fail and exit.
+
+.. note::
+ How to return a value from Task Selection Logic
+ Some languages explicitly support returning values from the script (e.g. MVEL and
+ JRuby) using an explicit return statement (e.g. ``return true``), other languages do not (e.g.
+ JavaScript and Jython). For languages that do not support the ``return`` statement, a special field called
+ ``returnValue`` must be created to hold the result of the task logic operation (i.e. assign a ``java.lang.Boolean``
+ value to the ``returnValue`` field before completing the task).
+ Also, in MVEL if there is not explicit return statement then the return value of the last executed statement will
+ return (e.g. the statement a=(1+2) will return the value 3).
+
+.. container:: paragraph
+
+ Each of the scripting languages used in Apex can import and use standard Java libraries to perform complex tasks.
+ Besides imported classes and normal language features Apex provides some natively available parameters and functions
+ that can be used directly. At run-time these parameters are populated by the Apex execution environment and made
+ natively available to logic scripts each time the logic script is invoked. (These can be accessed using the
+ ``executor`` keyword for most languages, or can be accessed directly without the ``executor`` keyword in MVEL):
+
+Table 2. The ``executor`` Fields / Methods
+ +-----------------------------------+------------------------------------+
+ | Unix, Cygwin | Windows |
+ +===================================+====================================+
+ |.. container:: content |.. container:: content |
+ | | |
+ | .. code:: bash | .. code:: bash |
+ | :number-lines: | :number-lines: |
+ | | |
+ | >c: | # cd /usr/local/src/apex-pdp |
+ | >cd \dev\apex | # mvn clean install -DskipTests |
+ | >mvn clean install -DskipTests | |
+ +-----------------------------------+------------------------------------+
+
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | Name | Type | Java type | Description |
+ +=====================================================+==========================================================================+===============================+==================================================================================+
+ | inFields | Fields | java.util.Map <String,Object> | All fields in the state’s incoming event. This is implemented as a standard Java |
+ | | | | Java (unmodifiable) Map |
+ | | | | |
+ | | | | **Example:** |
+ | | | | |
+ | | | | .. code:: javascript |
+ | | | | |
+ | | | | executor.logger.debug("Incoming fields: " + executor.inFields.entrySet()); |
+ | | | | var item_id = executor.incomingFields["item_ID"]; |
+ | | | | if (item_id >=1000) { ... } |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | outFields | Fields | java.util.Map <String,Object> | The outgoing task fields. This is implemented as a standard initially empty Java |
+ | | | | (modifiable) Map. To create a new schema-compliant instance of a field object |
+ | | | | see the utility method subject.getOutFieldSchemaHelper() below |
+ | | | | |
+ | | | | **Example:** |
+ | | | | |
+ | | | | .. code:: javascript |
+ | | | | |
+ | | | | executor.outFields["authorised"] = false; |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | logger | Logger | org.slf4j.ext.XLogger | A helpful logger |
+ | | | | |
+ | | | | **Example:** |
+ | | | | |
+ | | | | .. code:: javascript |
+ | | | | |
+ | | | | executor.logger.info("Executing task: " |
+ | | | | +executor.subject.id); |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | TRUE/FALSE | boolean | java.lang.Boolean | 2 helpful constants. These are useful to retrieve correct return values for the |
+ | | | | task logic |
+ | | | | |
+ | | | | **Example:** |
+ | | | | |
+ | | | | .. code:: javascript |
+ | | | | |
+ | | | | var returnValue = executor.isTrue; |
+ | | | | var returnValueType = Java.type("java.lang.Boolean"); |
+ | | | | var returnValue = new returnValueType(true); |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | subject | Task | TaskFacade | This provides some useful information about the task that contains this task |
+ | | | | logic. This object has some useful fields and methods : |
+ | | | | |
+ | | | | .. container:: ulist |
+ | | | | |
+ | | | | - **AxTask task** to get access to the full task definition of the host task |
+ | | | | |
+ | | | | - **String getTaskName()** to get the name of the host task |
+ | | | | |
+ | | | | - **String getId()** to get the ID of the host task |
+ | | | | |
+ | | | | - **SchemaHelper getInFieldSchemaHelper( String fieldName )** to |
+ | | | | get a ``SchemaHelper`` helper object to manipulate incoming |
+ | | | | task fields in a schema-aware manner |
+ | | | | |
+ | | | | - **SchemaHelper getOutFieldSchemaHelper( String fieldName )** to |
+ | | | | get a ``SchemaHelper`` helper object to manipulate outgoing |
+ | | | | task fields in a schema-aware manner, e.g. to instantiate new |
+ | | | | schema-compliant field objects to populate the |
+ | | | | ``executor.outFields`` outgoing fields map |
+ | | | | |
+ | | | | **Example:** |
+ | | | | |
+ | | | | .. code:: javascript |
+ | | | | |
+ | | | | executor.logger.info("Task name: " + executor.subject.getTaskName()); |
+ | | | | executor.logger.info("Task id: " + executor.subject.getId()); |
+ | | | | executor.outFields["authorised"] = executor.subject |
+ | | | | .getOutFieldSchemaHelper("authorised") |
+ | | | | .createNewInstance("false"); |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | parameters | Fields | java.util.Map <String,String> | All parameters in the current task. This is implemented as a standard Java Map. |
+ | | | | |
+ | | | | **Example:** |
+ | | | | |
+ | | | | .. code:: javascript |
+ | | | | |
+ | | | | executor.parameters.get("ParameterKey1")) |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | ContextAlbum getContextAlbum(String ctxtAlbumName ) | A utility method to retrieve a ``ContextAlbum`` for use in the task. | | |
+ | | This is how you access the context used by the task. The returned | | |
+ | | ``ContextAlbum`` implements the ``java.util.Map <String,Object>`` | | |
+ | | interface to get and set context as appropriate. The returned | | |
+ | | ``ContextAlbum`` also has methods to lock context albums, get | | |
+ | | information about the schema of the items to be stored in a context | | |
+ | | album, and get a ``SchemaHelper`` to manipulate context album items. How | | |
+ | | to define and use context in a task is described in the Apex | | |
+ | | Programmer’s Guide and in the My First Apex Policy guide. | | |
+ | | | | |
+ | | **Example:** | | |
+ | | | | |
+ | | .. code:: javascript | | |
+ | | | | |
+ | | var bkey = executor.inFields.get("branch_ID"); | | |
+ | | var cnts = executor.getContextMap("BranchCounts"); | | |
+ | | cnts.lockForWriting(bkey); | | |
+ | | cnts.put(bkey, cnts.get(bkey) + 1); | | |
+ | | cnts.unlockForWriting(bkey); | | |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+
+Logic Cheat Sheet
+=================
+
+.. container:: paragraph
+
+ Examples given here use Javascript (if not stated otherwise), other execution environments will be similar.