summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/apex/APEX-Policy-Guide.rst3265
1 files changed, 1475 insertions, 1790 deletions
diff --git a/docs/apex/APEX-Policy-Guide.rst b/docs/apex/APEX-Policy-Guide.rst
index cb2388e0..cb4ae729 100644
--- a/docs/apex/APEX-Policy-Guide.rst
+++ b/docs/apex/APEX-Policy-Guide.rst
@@ -1,2107 +1,1792 @@
-.. This work is licensed under a Creative Commons Attribution 4.0 International License.
-.. http://creativecommons.org/licenses/by/4.0
-
+ .. This work is licensed under a Creative Commons Attribution 4.0 International License.
+ .. http://creativecommons.org/licenses/by/4.0
+#################
APEX Policy Guide
-*****************************
+#################
+
.. contents::
:depth: 3
+******************
APEX Policy Matrix
-^^^^^^^^^^^^^^^^^^
+******************
-APEX Policy Matrix
-------------------
+.. container:: paragraph
- .. container:: paragraph
+ APEX offers a lot of flexibility for defining, deploying, and executing policies. Based on a theoretic model, it
+ supports virtually any policy model and supports translation of legacy policies into the APEX execution format.
+ However, the most important aspect for using APEX is to decide what policy is needed, what underlying policy concepts
+ should be used, and how the decision logic should be realized. Once these aspects are decided, APEX can be used to
+ execute the policies. If the policy evolves, say from a simple decision table to a fully adaptable policy, only the
+ policy definition requires change. APEX supports all of that.
- APEX offers a lot of flexibility for defining, deploying,
- and executing policies. Based on a theoretic model, it
- supports virtually any policy model and supports
- translation of legacy policies into the APEX execution format.
- However, the most important aspect for using APEX is to
- decide what policy is needed, what underlying policy
- concepts should be used, and how the decision logic
- should be realized. Once these aspects are decided, APEX
- can be used to execute the policies. If the policy
- evolves, say from a simple decision table to a fully
- adaptable policy, only the policy definition requires
- change. APEX supports all of that.
+.. container:: paragraph
- .. container:: paragraph
+ The figure below shows a (non-exhaustive) matrix, which will help to decide what policy is required to solve your
+ problem. Read the matrix from left to right choosing one cell in each column.
- The figure below shows a (non-exhaustive) matrix, which
- will help to decide what policy is required to solve your
- problem. Read the matrix from left to right choosing one
- cell in each column.
+.. container:: imageblock
- .. container:: imageblock
+ .. container:: content
- .. container:: content
+ |APEX Policy Matrix|
- |APEX Policy Matrix|
+ .. container:: title
- .. container:: title
-
- Figure 1. APEX Policy Matrix
-
- .. container:: paragraph
-
- The policy can support one of a number of stimuli with an
- associated purpose/model of the policy, for instance:
-
- .. container:: ulist
-
- - Configuration, i.e. what should happen. An example is
- an event that states an intended network configuration
- and the policy should provide the detailed actions for
- it. The policy can be realized for instance as an
- obligation policy, a promise or an intent.
-
- - Report, i.e. something did happen. An example is an
- event about an error or fault and the policy needs to
- repair that problem. The policy would usually be an
- obligation, utility function, or goal policy.
-
- - Monitoring, i.e. something does happen. An example is
- a notification about certain network conditions, to
- which the policy might (or might not) react. The
- policy will mitigate the monitored events or permit
- (deny) related actions as an obligation or
- authorization.
-
- - Analysis, i.e. why did something happen. An example is
- an analytic component sends insights of a situation
- requiring a policy to act on it. The policy can solve
- the problem, escalate it, or delegate it as a refrain
- or delegation policy.
-
- - Prediction, i.e. what will happen next. An example are
- events that a policy uses to predict a future network
- condition. The policy can prevent or enforce the
- prediction as an adaptive policy, a utility function,
- or a goal.
-
- - Feedback, i.e. why did something happen or not happen.
- Similar to analysis, but here the feedback will be in
- the input event and the policy needs to something with
- that information. Feedback can be related to history
- or experience, for instance a previous policy
- execution. The policy needs to be context-aware or be
- a meta-policy.
-
- .. container:: paragraph
-
- Once the purpose of the policy is decided, the next step
- is to look into what context information the policy will
- require to do its job. This can range from very simple to
- a lot of different information, for instance:
-
- .. container:: ulist
-
- - No context, nothing but a trigger event, e.g. a string
- or a number, is required
-
- - Event context, the incoming event provides all
- information (more than a string or number) for the
- policy
-
- - Policy context (read only), the policy has access to
- additional information related to its class but cannot
- change/alter them
-
- - Policy context (read and write), the policy has access
- to additional information related to its class and can
- alter this information (for instance to record
- historic information)
-
- - Global context (read only), the policy has access to
- additional information of any kind but cannot
- change/alter them
-
- - Global context (read and write), the policy the policy
- has access to additional information of any kind and
- can alter this information (for instance to record
- historic information)
+ Figure 1. APEX Policy Matrix
- .. container:: paragraph
+.. container:: paragraph
- The next step is to decide how the policy should do its
- job, i.e. what flavor it has, how many states are needed,
- and how many tasks. There are many possible combinations,
- for instance:
+ The policy can support one of a number of stimuli with an associated purpose/model of the policy, for instance:
- .. container:: ulist
+.. container:: ulist
- - Simple / God: a simple policy with 1 state and 1 task,
- which is doing everything for the decision-making.
- This is the ideal policy for simple situation, e.g.
- deciding on configuration parameters or simple access
- control.
+ - Configuration, i.e. what should happen. An example is an event that states an intended network configuration
+ and the policy should provide the detailed actions for it. The policy can be realized for instance as an
+ obligation policy, a promise or an intent.
- - Simple sequence: a simple policy with a number of
- states each having a single task. This is a very good
- policy for simple decision-making with different
- steps. For instance, a classic action policy (ECA)
- would have 3 states (E, C, and A) with some logic (1
- task) in each state.
-
- - Simple selective: a policy with 1 state but more than
- one task. Here, the appropriate task (and it’s logic)
- will be selected at execution time. This policy is
- very good for dealing with similar (or the same)
- situation in different contexts. For instance, the
- tasks can be related to available external software,
- or to current work load on the compute node, or to
- time of day.
-
- - Selective: any number of states having any number of
- tasks (usually more than 1 task). This is a
- combination of the two policies above, for instance an
- ECA policy with more than one task in E, C, and A.
-
- - Classic directed: a policy with more than one state,
- each having one task, but a non-sequential execution.
- This means that the sequence of the states is not
- pre-defined in the policy (as would be for all cases
- above) but calculated at runtime. This can be good to
- realize decision trees based on contextual
- information.
-
- - Super Adaptive: using the full potential of the APEX
- policy model, states and tasks and state execution are
- fully flexible and calculated at runtime (per policy
- execution). This policy is very close to a general
- programming system (with only a few limitations), but
- can solve very hard problems.
-
- .. container:: paragraph
-
- The final step is to select a response that the policy
- creates. Possible responses have been discussed in the
- literature for a very long time. A few examples are:
-
- .. container:: ulist
-
- - Obligation (deontic for what should happen)
-
- - Authorization (e.g. for rule-based or other access
- control or security systems)
+ - Report, i.e. something did happen. An example is an event about an error or fault and the policy needs to
+ repair that problem. The policy would usually be an obligation, utility function, or goal policy.
- - Intent (instead of providing detailed actions the
- response is an intent statement and a further system
- processes that)
+ - Monitoring, i.e. something does happen. An example is a notification about certain network conditions, to
+ which the policy might (or might not) react. The policy will mitigate the monitored events or permit (deny)
+ related actions as an obligation or authorization.
- - Delegation (hand the problem over to someone else,
- possibly with some information or instructions)
+ - Analysis, i.e. why did something happen. An example is an analytic component sends insights of a situation
+ requiring a policy to act on it. The policy can solve the problem, escalate it, or delegate it as a refrain or
+ delegation policy.
- - Fail / Error (the policy has encountered a problem,
- and reports it)
+ - Prediction, i.e. what will happen next. An example are events that a policy uses to predict a future network
+ condition. The policy can prevent or enforce the prediction as an adaptive policy, a utility function, or a goal.
- - Feedback (why did the policy make a certain decision)
+ - Feedback, i.e. why did something happen or not happen. Similar to analysis, but here the feedback will be in
+ the input event and the policy needs to something with that information. Feedback can be related to history or
+ experience, for instance a previous policy execution. The policy needs to be context-aware or be a meta-policy.
-APEX Policy Model
-^^^^^^^^^^^^^^^^^
+.. container:: paragraph
+
+ Once the purpose of the policy is decided, the next step is to look into what context information the policy will
+ require to do its job. This can range from very simple to a lot of different information, for instance:
+
+.. container:: ulist
+
+ - No context, nothing but a trigger event, e.g. a string or a number, is required
+
+ - Event context, the incoming event provides all information (more than a string or number) for the policy
+
+ - Policy context (read only), the policy has access to additional information related to its class but cannot
+ change/alter them
+
+ - Policy context (read and write), the policy has access to additional information related to its class and can
+ alter this information (for instance to record historic information)
+
+ - Global context (read only), the policy has access to additional information of any kind but cannot
+ change/alter them
+
+ - Global context (read and write), the policy the policy has access to additional information of any kind and
+ can alter this information (for instance to record historic information)
+
+.. container:: paragraph
+
+ The next step is to decide how the policy should do its job, i.e. what flavor it has, how many states are needed,
+ and how many tasks. There are many possible combinations, for instance:
+
+.. container:: ulist
+
+ - Simple / God: a simple policy with 1 state and 1 task, which is doing everything for the decision-making. This
+ is the ideal policy for simple situation, e.g. deciding on configuration parameters or simple access control.
+
+ - Simple sequence: a simple policy with a number of states each having a single task. This is a very good policy
+ for simple decision-making with different steps. For instance, a classic action policy (ECA) would have 3 states
+ (E, C, and A) with some logic (1 task) in each state.
+
+ - Simple selective: a policy with 1 state but more than one task. Here, the appropriate task (and it’s logic)
+ will be selected at execution time. This policy is very good for dealing with similar (or the same) situation in
+ different contexts. For instance, the tasks can be related to available external software, or to current work load
+ on the compute node, or to time of day.
+
+ - Selective: any number of states having any number of tasks (usually more than 1 task). This is a combination
+ of the two policies above, for instance an ECA policy with more than one task in E, C, and A.
+
+ - Classic directed: a policy with more than one state, each having one task, but a non-sequential execution.
+ This means that the sequence of the states is not pre-defined in the policy (as would be for all cases above) but
+ calculated at runtime. This can be good to realize decision trees based on contextual information.
+
+ - Super Adaptive: using the full potential of the APEX policy model, states and tasks and state execution are
+ fully flexible and calculated at runtime (per policy execution). This policy is very close to a general
+ programming system (with only a few limitations), but can solve very hard problems.
+
+.. container:: paragraph
-Introduction
-------------
+ The final step is to select a response that the policy creates. Possible responses have been discussed in the
+ literature for a very long time. A few examples are:
- .. container:: paragraph
+.. container:: ulist
- The APEX policy model is shown in UML notation in the
- figure below. A policy model can be stored in JSON or XML
- format in a file or can be held in a database. The APEX
- editor creates and modifies APEX policy models. APEX
- deployment deploys policy models, and a policy model is
- loaded into APEX engines so that the engines can run the
- policies in the policy model.
+ - Obligation (deontic for what should happen)
- .. container:: paragraph
+ - Authorization (e.g. for rule-based or other access control or security systems)
- The figure shows four different views of the policy
- model:
+ - Intent (instead of providing detailed actions the response is an intent statement and a further system
+ processes that)
- .. container:: ulist
+ - Delegation (hand the problem over to someone else, possibly with some information or instructions)
- - The general model view shows the main parts of a
- policy: state, state output, event, and task. A task
- can also have parameters. Data types can be defined on
- a per-model basis using either standard atomic types
- (such as character, string, numbers) or complex types
- from a policy domain.
+ - Fail / Error (the policy has encountered a problem, and reports it)
- - The logic model view emphasizes how decision-making
- logic is injected into a policy. There are essentially
- three different types of logic: task logic (for
- decision making in a task), task selection logic (to
- select a task if more than one is defined in a state),
- and state finalizer logic (to compute the final output
- event of a state and select an appropriate next state
- from the policy model).
+ - Feedback (why did the policy make a certain decision)
- - The context model view shows how context is injected
- into a policy. States collect all context from their
- tasks. A task can define what context it requires for
- the decision making, i.e. what context the task logic
- will process. Context itself is a collection of items
- (individual context information) with data types.
- Context can be templated.
+*****************
+APEX Policy Model
+*****************
+
+.. container:: paragraph
+
+ The APEX policy model is shown in UML notation in the figure below. A policy model can be stored in JSON or XML
+ format in a file or can be held in a database. The APEX editor creates and modifies APEX policy models. APEX
+ deployment deploys policy models, and a policy model is loaded into APEX engines so that the engines can run the
+ policies in the policy model.
+
+.. container:: paragraph
+
+ The figure shows four different views of the policy model:
+
+.. container:: ulist
- - The event and field model view shows the events in the
- policy model. Tasks define what information they
- consume (input) and produce (output). This information
- is modeled as fields, essentially a key/type tuple in
- the model and a key/type/value triple at execution.
- Events then are collection of fields.
+ - The general model view shows the main parts of a policy: state, state output, event, and task. A task can also
+ have parameters. Data types can be defined on a per-model basis using either standard atomic types (such as
+ character, string, numbers) or complex types from a policy domain.
- .. container:: imageblock
+ - The logic model view emphasizes how decision-making logic is injected into a policy. There are essentially
+ three different types of logic: task logic (for decision making in a task), task selection logic (to select a task
+ if more than one is defined in a state), and state finalizer logic (to compute the final output event of a state
+ and select an appropriate next state from the policy model).
- .. container:: content
+ - The context model view shows how context is injected into a policy. States collect all context from their
+ tasks. A task can define what context it requires for the decision making, i.e. what context the task logic will
+ process. Context itself is a collection of items (individual context information) with data types. Context can be
+ templated.
- |APEX Policy Model for Execution|
+ - The event and field model view shows the events in the policy model. Tasks define what information they
+ consume (input) and produce (output). This information is modeled as fields, essentially a key/type tuple in the
+ model and a key/type/value triple at execution. Events then are collection of fields.
- .. container:: title
+.. container:: imageblock
- Figure 2. APEX Policy Model for Execution
+ .. container:: content
+
+ |APEX Policy Model for Execution|
+
+ .. container:: title
+
+ Figure 2. APEX Policy Model for Execution
Concepts and Keys
-#################
+=================
- .. container:: paragraph
+.. container:: paragraph
- Each element of the policy model is called a
- *concept*. Each *concept* is a subclass of the
- abstract *Concept* class, as shown in the next figure.
- Every concept implements the following abstract
- methods:
+ Each element of the policy model is called a *concept*. Each *concept* is a subclass of the abstract *Concept*
+ class, as shown in the next figure. Every concept implements the following abstract methods:
- .. container:: imageblock
+.. container:: imageblock
- .. container:: content
+ .. container:: content
- |Concepts and Keys|
+ |Concepts and Keys|
- .. container:: title
+ .. container:: title
- Figure 3. Concepts and Keys
+ Figure 3. Concepts and Keys
- .. container:: ulist
+.. container:: ulist
- - ``getKey()`` - gets the unique key for this concept
- instance in the system
+ - ``getKey()`` - gets the unique key for this concept instance in the system
- - ``validate()`` - validates the structure of this
- concept, its sub-concepts and its relationships
+ - ``validate()`` - validates the structure of this concept, its sub-concepts and its relationships
- - ``clean()`` - carries out housekeeping on the
- concept such as trimming strings, remove any
- hanging references
+ - ``clean()`` - carries out housekeeping on the concept such as trimming strings, remove any hanging references
- - ``clone()`` - creates a deep copy of an instance of
- this concept
+ - ``clone()`` - creates a deep copy of an instance of this concept
- - ``equals()`` - checks if two instances of this
- concept are equal
+ - ``equals()`` - checks if two instances of this concept are equal
- - ``toString()`` - returns a string representation of
- the concept
+ - ``toString()`` - returns a string representation of the concept
- - ``hashCode()`` - returns a hash code for the
- concept
+ - ``hashCode()`` - returns a hash code for the concept
- - ``copyTo()`` - carries out a deep copy of one
- instance of the concept to another instance,
- overwriting the target fields.
+ - ``copyTo()`` - carries out a deep copy of one instance of the concept to another instance, overwriting the
+ target fields.
- .. container:: paragraph
+.. container:: paragraph
- All concepts must have a *key*, which uniquely
- identifies a concept instance. The *key* of a subclass
- of an *Concept* must either be an ``ArtifactKey`` or
- an ``ReferenceKey``. Concepts that have a stand-alone
- independent existence such as *Policy*, *Task*, and
- *Event* must have an ``ArtifctKey`` key. Concepts that
- are contained in other concepts, that do not exist as
- stand-alone concepts must have an ``ReferenceKey``
- key. Examples of such concepts are *State* and
- *EventParameter*.
+ All concepts must have a *key*, which uniquely identifies a concept instance. The *key* of a subclass of an *Concept*
+ must either be an ``ArtifactKey`` or an ``ReferenceKey``. Concepts that have a stand-alone independent existence such
+ as *Policy*, *Task*, and *Event* must have an ``ArtifctKey`` key. Concepts that are contained in other concepts, that
+ do not exist as stand-alone concepts must have an ``ReferenceKey`` key. Examples of such concepts are *State* and
+ *EventParameter*.
- .. container:: paragraph
+.. container:: paragraph
- An ``ArticactKey`` has two fields; the *Name* of the
- concept it is the key for and the concept’s *Version*.
- A concept’s name must be unique in a given
- PolicyModel. A concept version is represented using
- the well known *major.minor.path* scheme as used in
- semantic versioning.
+ An ``ArticactKey`` has two fields; the *Name* of the concept it is the key for and the concept’s *Version*. A
+ concept’s name must be unique in a given PolicyModel. A concept version is represented using the well known
+ *major.minor.path* scheme as used in semantic versioning.
- .. container:: paragraph
+.. container:: paragraph
- A ``ReferenceKey`` has three fields. The *UserKeyName*
- and *UserKeyVersion* fields identify the
- ``ArtifactKey`` of the concept in which the concept
- keyed by the ``ReferenceKey`` is contained. The
- *LocalName* field identifies the contained concept
- instance. The *LocalName* must be unique in the
- concepts of a given type contained by a parent.
+ A ``ReferenceKey`` has three fields. The *UserKeyName* and *UserKeyVersion* fields identify the ``ArtifactKey`` of
+ the concept in which the concept keyed by the ``ReferenceKey`` is contained. The *LocalName* field identifies the
+ contained concept instance. The *LocalName* must be unique in the concepts of a given type contained by a parent.
- .. container:: paragraph
+.. container:: paragraph
- For example, a policy called ``SalesPolicy`` with a
- Version of ``1.12.4`` has a state called ``Decide``.
- The ``Decide`` state is linked to the ``SalesPolicy``
- with a ``ReferenceKey`` with fields *UserKeyName* of
- ``SalesPolicy``, *UserKeyVersion* of ``1.12.4``, and
- *LocalName* of ``Decide``. There must not be another
- state called ``Decide`` in the policy ``SalesPolicy``.
- However, there may well be a state called ``Decide``
- in some other policy called ``PurchasingPolicy``.
+ For example, a policy called ``SalesPolicy`` with a Version of ``1.12.4`` has a state called ``Decide``. The
+ ``Decide`` state is linked to the ``SalesPolicy`` with a ``ReferenceKey`` with fields *UserKeyName* of
+ ``SalesPolicy``, *UserKeyVersion* of ``1.12.4``, and *LocalName* of ``Decide``. There must not be another state
+ called ``Decide`` in the policy ``SalesPolicy``. However, there may well be a state called ``Decide`` in some other
+ policy called ``PurchasingPolicy``.
- .. container:: paragraph
+.. container:: paragraph
- Each concept in the model is also a JPA (`Java
- Persistence
- API <https://en.wikipedia.org/wiki/Java_Persistence_API>`__)
- Entity. This means that every concept can be
- individually persisted or the entire model can be
- persisted en-bloc to any persistence mechanism using
- an JPA framework such as
- `Hibernate <http://hibernate.org/>`__ or
- `EclipseLink <http://www.eclipse.org/eclipselink/>`__.
+ Each concept in the model is also a JPA
+ (`Java Persistence API <https://en.wikipedia.org/wiki/Java_Persistence_API>`__) Entity. This means that every concept
+ can be individually persisted or the entire model can be persisted en-bloc to any persistence mechanism using an JPA
+ framework such as `Hibernate <http://hibernate.org/>`__ or `EclipseLink <http://www.eclipse.org/eclipselink/>`__.
Concept: PolicyModel
-####################
+====================
- .. container:: paragraph
+.. container:: paragraph
- The *PolicyModel* concept is a container that holds
- the definition of a set of policies and their
- associated events, context maps, and tasks. A
- *PolicyModel* is implemented as four maps for
- policies, events, context maps, and tasks. Each map is
- indexed by the key of the policy, event, context map,
- or task. Any non-empty policy model must have at least
- one entry in its policy, event, and task map because
- all policies must have at least one input and output
- event and must execute at least one task.
+ The *PolicyModel* concept is a container that holds the definition of a set of policies and their associated events,
+ context maps, and tasks. A *PolicyModel* is implemented as four maps for policies, events, context maps, and tasks.
+ Each map is indexed by the key of the policy, event, context map, or task. Any non-empty policy model must have at
+ least one entry in its policy, event, and task map because all policies must have at least one input and output event
+ and must execute at least one task.
- .. container:: paragraph
+.. container:: paragraph
- A *PolicyModel* concept is keyed with an
- ``ArtifactKey key``. Because a *PolicyModel* is an
- ``AxConcept``, calling the ``validate()`` method on a
- policy model validates the concepts, structure, and
- relationships of the entire policy model.
+ A *PolicyModel* concept is keyed with an ``ArtifactKey key``. Because a *PolicyModel* is an ``AxConcept``, calling
+ the ``validate()`` method on a policy model validates the concepts, structure, and relationships of the entire policy
+ model.
Concept: DataType
-#################
+=================
+
+.. container:: paragraph
- .. container:: paragraph
-
- Data types are tightly controlled in APEX in order to
- provide a very high degree of consistency in policies
- and to facilitate tracking of changes to context as
- policies execute. All context is modeled as a
- *DataType* concept. Each DataType concept instance is
- keyed with an ``ArtifactKey`` key. The DataType field
- identifies the Java class of objects that is used to
- represent concept instances that use this data type.
- All context has a *DataType*; incoming and outgoing
- context is represented by *EventField* concepts and
- all other context is represented by *ContextItem*
- concepts.
+ Data types are tightly controlled in APEX in order to provide a very high degree of consistency in policies and to
+ facilitate tracking of changes to context as policies execute. All context is modeled as a *DataType* concept. Each
+ DataType concept instance is keyed with an ``ArtifactKey`` key. The DataType field identifies the Java class of
+ objects that is used to represent concept instances that use this data type. All context has a *DataType*; incoming
+ and outgoing context is represented by *EventField* concepts and all other context is represented by *ContextItem*
+ concepts.
Concept: Event
-##############
+==============
- .. container:: paragraph
+.. container:: paragraph
- An *Event* defines the structure of a message that
- passes into or out of an APEX engine or that passes
- between two states in an APEX engine. APEX supports
- message reception and sending in many formats and all
- messages are translated into an *Event* prior to
- processing by an APEX engine. Event concepts are keyed
- with an ``ArtifactKey`` key. The parameters of an
- event are held as a map of *EventField* concept
- instances with each parameter indexed by the
- *LocalName* of its ``ReferenceKey``. An *Event* has
- three fields:
+ An *Event* defines the structure of a message that passes into or out of an APEX engine or that passes between two
+ states in an APEX engine. APEX supports message reception and sending in many formats and all messages are translated
+ into an *Event* prior to processing by an APEX engine. Event concepts are keyed with an ``ArtifactKey`` key. The
+ parameters of an event are held as a map of *EventField* concept instances with each parameter indexed by the
+ *LocalName* of its ``ReferenceKey``. An *Event* has three fields:
- .. container:: ulist
+.. container:: ulist
- - The *NameSpace* identifies the domain of
- application of the event
+ - The *NameSpace* identifies the domain of application of the event
- - The *Source* of the event identifies the system
- that emitted the event
+ - The *Source* of the event identifies the system that emitted the event
- - The *Target* of the event identifies the system
- that the event was sent to
+ - The *Target* of the event identifies the system that the event was sent to
- .. container:: paragraph
+.. container:: paragraph
- A *PolicyModel* contains a map of all the events known
- to a given policy model. Although an empty model may
- have no events in its event map, any sane policy model
- must have at least one *Event* defined.
+ A *PolicyModel* contains a map of all the events known to a given policy model. Although an empty model may have no
+ events in its event map, any sane policy model must have at least one *Event* defined.
Concept: EventField
-###################
+===================
- .. container:: paragraph
+.. container:: paragraph
- The incoming context and outgoing context of an event
- are the fields of the event. Each field representing a
- single piece of incoming or outgoing context. Each
- field of an *Event* is represented by an instance of
- the *EventField* concept. Each *EventField* concept
- instance in an event is keyed with a ``ReferenceKey``
- key, which references the event. The *LocalName* field
- of the ``ReferenceKey`` holds the name of the field A
- reference to a *DataType* concept defines the data
- type that values of this parameter have at run time.
+ The incoming context and outgoing context of an event are the fields of the event. Each field representing a single
+ piece of incoming or outgoing context. Each field of an *Event* is represented by an instance of the *EventField*
+ concept. Each *EventField* concept instance in an event is keyed with a ``ReferenceKey`` key, which references the
+ event. The *LocalName* field of the ``ReferenceKey`` holds the name of the field A reference to a *DataType* concept
+ defines the data type that values of this parameter have at run time.
Concept: ContextMap
-###################
-
- .. container:: paragraph
-
- The set of context that is available for use by the
- policies of a *PolicyModel* is defined as *ContextMap*
- concept instances. The *PolicyModel* holds a map of
- all the *ContextMap* definitions. A *ContextMap* is
- itself a container for a group of related context
- items, each of which is represented by a *ContextItem*
- concept instance. *ContextMap* concepts are keyed with
- an ``ArtifactKey`` key. A developer can use the APEX
- Policy Editor to create context maps for their
- application domain.
-
- .. container:: paragraph
-
- A *ContextMap* uses a map to hold the context items.
- The ContextItem concept instances in the map are
- indexed by the *LocalName* of their ``ReferenceKey``.
-
- .. container:: paragraph
-
- The *ContextMapType* field of a *ContextMap* defines
- the type of a context map. The type can have either of
- two values:
-
- .. container:: ulist
-
- - A *BAG* context map is a context map with fixed
- content. Each possible context item in the context
- map is defined at design time and is held in the
- *ContextMap* context instance as *ContextItem*
- concept definitions and only the values of the
- context items in the context map can be changed at
- run time. The context items in a *BAG* context map
- have mixed types and distinct *ContextItem* concept
- instances of the same type can be defined. A *BAG*
- context map is convenient for defining a group of
- context items that are diverse but are related by
- domain, such as the characteristics of a device. A
- fully defined *BAG* context map has a fully
- populated *ContextItem* map but its
- *ContextItemTemplate* reference is not defined.
-
- - A *SAMETYPE* context map is used to represent a
- group of *ContextItem* instances of the same type.
- Unlike a *BAG* context map, the *ContextItem*
- concept instances of a *SAMETYPE* context map can
- be added, modified, and deleted at runtime. All
- *ContextItem* concept instances in a *SAMETYPE*
- context map must be of the same type, and that
- context item is defined as a single
- *ContextItemTemplate* concept instances at design
- time. At run time, the *ContextItemTemplate*
- definition is used to create new *ContextItem*
- concept instances for the context map on demand. A
- fully defined *SAMETYPE context map has an empty
- ContextItem map and its ContextItemTemplate\_*
- reference is defined.
-
- .. container:: paragraph
-
- The *Scope* of a *ContextMap* defines the range of
- applicability of a context map in APEX. The following
- scopes of applicability are defined:
-
- .. container:: ulist
-
- - *EPHEMERAL* scope means that the context map is
- owned, used, and modified by a single application,
- but the context map only exists while that
- application is running
-
- - *APPLICATION* scope specifies that the context map
- is owned, used, and modified by a single
- application, the context map is persistent
-
- - *GLOBAL* scope specifies that the context map is
- globally owned and is used and modified by any
- application, the context map is persistent
-
- - *EXTERNAL* scope specifies that the context map is
- owned by an external system and may be used in a
- read-only manner by any application, the context
- map is persistent
-
- .. container:: paragraph
-
- A much more sophisticated scoping mechanism for
- context maps is envisaged for Apex in future work. In
- such a mechanism, the scope of a context map would
- work somewhat like the way roles work in security
- authentication systems.
+===================
+
+.. container:: paragraph
+
+ The set of context that is available for use by the policies of a *PolicyModel* is defined as *ContextMap* concept
+ instances. The *PolicyModel* holds a map of all the *ContextMap* definitions. A *ContextMap* is itself a container
+ for a group of related context items, each of which is represented by a *ContextItem* concept instance. *ContextMap*
+ concepts are keyed with an ``ArtifactKey`` key. A developer can use the APEX Policy Editor to create context maps for
+ their application domain.
+
+.. container:: paragraph
+
+ A *ContextMap* uses a map to hold the context items. The ContextItem concept instances in the map are indexed by the
+ *LocalName* of their ``ReferenceKey``.
+
+.. container:: paragraph
+
+ The *ContextMapType* field of a *ContextMap* defines the type of a context map. The type can have either of two
+ values:
+
+.. container:: ulist
+
+ - A *BAG* context map is a context map with fixed content. Each possible context item in the context map is
+ defined at design time and is held in the *ContextMap* context instance as *ContextItem* concept definitions and
+ only the values of the context items in the context map can be changed at run time. The context items in a *BAG*
+ context map have mixed types and distinct *ContextItem* concept instances of the same type can be defined. A *BAG*
+ context map is convenient for defining a group of context items that are diverse but are related by domain, such as
+ the characteristics of a device. A fully defined *BAG* context map has a fully populated *ContextItem* map but its
+ *ContextItemTemplate* reference is not defined.
+
+ - A *SAMETYPE* context map is used to represent a group of *ContextItem* instances of the same type. Unlike a
+ *BAG* context map, the *ContextItem* concept instances of a *SAMETYPE* context map can be added, modified, and
+ deleted at runtime. All *ContextItem* concept instances in a *SAMETYPE* context map must be of the same type, and
+ that context item is defined as a single *ContextItemTemplate* concept instances at design time. At run time, the
+ *ContextItemTemplate* definition is used to create new *ContextItem* concept instances for the context map on
+ demand. A fully defined *SAMETYPE context map has an empty ContextItem map and its ContextItemTemplate\_*
+ reference is defined.
+
+.. container:: paragraph
+
+ The *Scope* of a *ContextMap* defines the range of applicability of a context map in APEX. The following scopes of
+ applicability are defined:
+
+.. container:: ulist
+
+ - *EPHEMERAL* scope means that the context map is owned, used, and modified by a single application but the
+ context map only exists while that application is running
+
+ - *APPLICATION* scope specifies that the context map is owned, used, and modified by a single application, the
+ context map is persistent
+
+ - *GLOBAL* scope specifies that the context map is globally owned and is used and modified by any application,
+ the context map is persistent
+
+ - *EXTERNAL* scope specifies that the context map is owned by an external system and may be used in a read-only
+ manner by any application, the context map is persistent
+
+.. container:: paragraph
+
+ A much more sophisticated scoping mechanism for context maps is envisaged for Apex in future work. In such a
+ mechanism, the scope of a context map would work somewhat like the way roles work in security authentication systems.
Concept: ContextItem
-####################
-
- .. container:: paragraph
-
- Each piece of context in a *ContextMap* is represented
- by an instance of the *ContextItem* concept. Each
- *ContextItem* concept instance in a context map keyed
- with a ``ReferenceKey`` key, which references the
- context map of the context item. The *LocalName* field
- of the ``ReferenceKey`` holds the name of the context
- item in the context map A reference to a *DataType*
- concept defines the data type that values of this
- context item have at run time. The *WritableFlag*
- indicates if the context item is read only or
- read-write at run time.
+====================
+
+.. container:: paragraph
+
+ Each piece of context in a *ContextMap* is represented by an instance of the *ContextItem* concept. Each
+ *ContextItem* concept instance in a context map keyed with a ``ReferenceKey`` key, which references the context map
+ of the context item. The *LocalName* field of the ``ReferenceKey`` holds the name of the context item in the context
+ map A reference to a *DataType* concept defines the data type that values of this context item have at run time. The
+ *WritableFlag* indicates if the context item is read only or read-write at run time.
Concept: ContextItemTemplate
-############################
-
- .. container:: paragraph
-
- In a *SAMETYPE* *ContextMap*, the
- *ContextItemTemplate* definition provides a template
- for the *ContextItem* instances that will be created
- on the context map at run time. Each *ContextItem*
- concept instance in the context map is created using
- the *ContextItemTemplate* template. It is keyed with a
- ``ReferenceKey`` key, which references the context map
- of the context item. The *LocalName* field of the
- ``ReferenceKey``, supplied by the creator of the
- context item at run time, holds the name of the
- context item in the context map. A reference to a
- *DataType* concept defines the data type that values
- of this context item have at run time. The
- *WritableFlag* indicates if the context item is read
- only or read-write at run time.
+============================
+
+.. container:: paragraph
+
+ In a *SAMETYPE* *ContextMap*, the *ContextItemTemplate* definition provides a template for the *ContextItem*
+ instances that will be created on the context map at run time. Each *ContextItem* concept instance in the context map
+ is created using the *ContextItemTemplate* template. It is keyed with a ``ReferenceKey`` key, which references the
+ context map of the context item. The *LocalName* field of the ``ReferenceKey``, supplied by the creator of the
+ context item at run time, holds the name of the context item in the context map. A reference to a *DataType* concept
+ defines the data type that values of this context item have at run time. The *WritableFlag* indicates if the context
+ item is read only or read-write at run time.
Concept: Task
-#############
-
- .. container:: paragraph
-
- The smallest unit of logic in a policy is a *Task*. A
- task encapsulates a single atomic unit of logic, and
- is designed to be a single indivisible unit of
- execution. A task may be invoked by a single policy or
- by many policies. A task has a single trigger event,
- which is sent to the task when it is invoked. Tasks
- emit one or more outgoing events, which carry the
- result of the task execution. Tasks may use or modify
- context as they execute.
-
- .. container:: paragraph
-
- The Task concept definition captures the definition of
- an APEX task. Task concepts are keyed with an
- ``ArtifactKey`` key. The Trigger of the task is a
- reference to the *Event* concept that triggers the
- task. The *OutgoingEvents* of a task are a set of
- references to *Event* concepts that may be emitted by
- the task.
-
- .. container:: paragraph
-
- All tasks have logic, some code that is programmed to
- execute the work of the task. The *Logic* concept of
- the task holds the definition of that logic.
-
- .. container:: paragraph
-
- The *Task* definition holds a set of *ContextItem* and
- *ContextItemTemplate* context items that the task is
- allow to access, as defined by the task developer at
- design time. The type of access (read-only or read
- write) that a task has is determined by the
- *WritableFlag* flag on the individual context item
- definitions. At run time, a task may only access the
- context items specified in its context item set, the
- APEX engine makes only the context items in the task
- context item set is available to the task.
-
- .. container:: paragraph
-
- A task can be configured with startup parameters. The
- set of parameters that can be configured on a task are
- defined as a set of *TaskParameter* concept
- definitions.
+=============
+
+.. container:: paragraph
+
+ The smallest unit of logic in a policy is a *Task*. A task encapsulates a single atomic unit of logic, and is
+ designed to be a single indivisible unit of execution. A task may be invoked by a single policy or by many policies.
+ A task has a single trigger event, which is sent to the task when it is invoked. Tasks emit one or more outgoing
+ events, which carry the result of the task execution. Tasks may use or modify context as they execute.
+
+.. container:: paragraph
+
+ The Task concept definition captures the definition of an APEX task. Task concepts are keyed with an ``ArtifactKey``
+ key. The Trigger of the task is a reference to the *Event* concept that triggers the task. The *OutgoingEvents* of a
+ task are a set of references to *Event* concepts that may be emitted by the task.
+
+.. container:: paragraph
+
+ All tasks have logic, some code that is programmed to execute the work of the task. The *Logic* concept of the task
+ holds the definition of that logic.
+
+.. container:: paragraph
+
+ The *Task* definition holds a set of *ContextItem* and *ContextItemTemplate* context items that the task is allow to
+ access, as defined by the task developer at design time. The type of access (read-only or read write) that a task has
+ is determined by the *WritableFlag* flag on the individual context item definitions. At run time, a task may only
+ access the context items specified in its context item set, the APEX engine makes only the context items in the task
+ context item set is available to the task.
+
+.. container:: paragraph
+
+ A task can be configured with startup parameters. The set of parameters that can be configured on a task are defined
+ as a set of *TaskParameter* concept definitions.
Concept: TaskParameter
-######################
+======================
- .. container:: paragraph
+.. container:: paragraph
- Each configuration parameter of a task are represented
- as a *Taskparameter* concept keyed with a
- ``ReferenceKey`` key, which references the task. The
- *LocalName* field of the ``ReferenceKey`` holds the
- name of the parameter. The *DefaultValue* field
- defines the default value that the task parameter is
- set to. The value of *TaskParameter* instances can be
- overridden at deployment time by specifying their
- values in the configuration information passed to APEX
- engines.
+ Each configuration parameter of a task are represented as a *Taskparameter* concept keyed with a ``ReferenceKey``
+ key, which references the task. The *LocalName* field of the ``ReferenceKey`` holds the name of the parameter. The
+ *DefaultValue* field defines the default value that the task parameter is set to. The value of *TaskParameter*
+ instances can be overridden at deployment time by specifying their values in the configuration information passed to
+ APEX engines.
- .. container:: paragraph
+.. container:: paragraph
- The *taskParameters* field is specified under *engineParameters*
- in the ApexConfig. It can contain one or more task parameters, where each
- item can contain the parameter key, value as well as the taskId to which it is associated.
- If the taskId is not specified, then the parameters are added to all tasks.
+ The *taskParameters* field is specified under *engineParameters* in the ApexConfig. It can contain one or more task
+ parameters, where each item can contain the parameter key, value as well as the taskId to which it is associated. If
+ the taskId is not specified, then the parameters are added to all tasks.
Concept: Logic
-##############
-
- .. container:: paragraph
-
- The *Logic* concept instance holds the actual
- programmed task logic for a task defined in a *Task*
- concept or the programmed task selection logic for a
- state defined in a *State* concept. It is keyed with a
- ``ReferenceKey`` key, which references the task or
- state that owns the logic. The *LocalName* field of
- the Logic concept is the name of the logic.
-
- .. container:: paragraph
-
- The *LogicCode* field of a Logic concept definition is
- a string that holds the program code that is to be
- executed at run time. The *LogicType* field defines
- the language of the code. The standard values are the
- logic languages supported by APEX:
- `JAVASCRIPT <https://en.wikipedia.org/wiki/JavaScript>`__,
- `JAVA <https://java.com/en/>`__,
- `JYTHON <http://www.jython.org/>`__,
- `JRUBY <http://jruby.org/>`__, or
- `MVEL <https://en.wikibooks.org/wiki/Transwiki:MVEL_Language_Guide>`__.
-
- .. container:: paragraph
-
- The APEX engine uses the *LogicType* field value to
- decide which language interpreter to use for a task
- and then sends the logic defined in the *LogicCode*
- field to that interpreter.
+==============
+
+.. container:: paragraph
+
+ The *Logic* concept instance holds the actual programmed task logic for a task defined in a *Task* concept or the
+ programmed task selection logic for a state defined in a *State* concept. It is keyed with a ``ReferenceKey`` key,
+ which references the task or state that owns the logic. The *LocalName* field of the Logic concept is the name of the
+ logic.
+
+.. container:: paragraph
+
+ The *LogicCode* field of a Logic concept definition is a string that holds the program code that is to be executed
+ at run time. The *LogicType* field defines the language of the code. The standard values are the logic languages
+ supported by APEX: `JAVASCRIPT <https://en.wikipedia.org/wiki/JavaScript>`__, `JAVA <https://java.com/en/>`__,
+ `JYTHON <http://www.jython.org/>`__, `JRUBY <http://jruby.org/>`__, or
+ `MVEL <https://en.wikibooks.org/wiki/Transwiki:MVEL_Language_Guide>`__.
+
+.. container:: paragraph
+
+ The APEX engine uses the *LogicType* field value to decide which language interpreter to use for a task and then
+ sends the logic defined in the *LogicCode* field to that interpreter.
Concept: Policy
-###############
-
- .. container:: paragraph
-
- The *Policy* concept defines a policy in APEX. The
- definition is rather straightforward. A policy is made
- up of a set of states with the flavor of the policy
- determining the structure of the policy states and the
- first state defining what state in the policy executes
- first. *Policy* concepts are keyed with an
- ``ArtifactKey`` key.
-
- .. container:: paragraph
-
- The *PolicyFlavour* of a *Policy* concept specifies
- the structure that will be used for the states in the
- policy. A number of commonly used policy patterns are
- supported as APEX policy flavors. The standard policy
- flavors are:
-
- .. container:: ulist
-
- - The *MEDA* flavor supports policies written to the
- `MEDA policy
- pattern <https://www.researchgate.net/publication/282576518_Dynamically_Adaptive_Policies_for_Dynamically_Adaptive_Telecommunications_Networks>`__
- and require a sequence of four states: namely
- *Match*, *Establish*, *Decide* and *Act*.
-
- - The *OODA* flavor supports policies written to the
- `OODA loop
- pattern <https://en.wikipedia.org/wiki/OODA_loop>`__
- and require a sequence of four states: namely
- *Observe*, *Orient*, *Decide* and *Act*.
-
- - The *ECA* flavor supports policies written to the
- `ECA active rule
- pattern <https://en.wikipedia.org/wiki/Event_condition_action>`__
- and require a sequence of three states: namely
- *Event*, *Condition* and *Action*
-
- - The *XACML* flavor supports policies written in
- `XACML <https://en.wikipedia.org/wiki/XACML>`__ and
- require a single state: namely *XACML*
-
- - The *FREEFORM* flavor supports policies written in
- an arbitrary style. A user can define a *FREEFORM*
- policy as an arbitrarily long chain of states.
-
- .. container:: paragraph
-
- The *FirstState* field of a *Policy* definition is the
- starting point for execution of a policy. Therefore,
- the trigger event of the state referenced in the
- *FirstState* field is also the trigger event for the
- entire policy.
+===============
+
+.. container:: paragraph
+
+ The *Policy* concept defines a policy in APEX. The definition is rather straightforward. A policy is made up of a
+ set of states with the flavor of the policy determining the structure of the policy states and the first state
+ defining what state in the policy executes first. *Policy* concepts are keyed with an ``ArtifactKey`` key.
+
+.. container:: paragraph
+
+ The *PolicyFlavour* of a *Policy* concept specifies the structure that will be used for the states in the policy. A
+ number of commonly used policy patterns are supported as APEX policy flavors. The standard policy flavors are:
+
+.. container:: ulist
+
+ - The *MEDA* flavor supports policies written to the
+ `MEDA policy pattern <https://www.researchgate.net/publication/282576518_Dynamically_Adaptive_Policies_for_Dynamically_Adaptive_Telecommunications_Networks>`__
+ and require a sequence of four states: namely *Match*, *Establish*, *Decide* and *Act*.
+
+ - The *OODA* flavor supports policies written to the
+ `OODA loop pattern <https://en.wikipedia.org/wiki/OODA_loop>`__ and require a sequence of four states: namely
+ *Observe*, *Orient*, *Decide* and *Act*.
+
+ - The *ECA* flavor supports policies written to the
+ `ECA active rule pattern <https://en.wikipedia.org/wiki/Event_condition_action>`__ and require a sequence of three
+ states: namely *Event*, *Condition* and *Action*
+
+ - The *XACML* flavor supports policies written in `XACML <https://en.wikipedia.org/wiki/XACML>`__ and require a
+ single state: namely *XACML*
+
+ - The *FREEFORM* flavor supports policies written in an arbitrary style. A user can define a *FREEFORM* policy
+ as an arbitrarily long chain of states.
+
+.. container:: paragraph
+
+ The *FirstState* field of a *Policy* definition is the starting point for execution of a policy. Therefore, the
+ trigger event of the state referenced in the *FirstState* field is also the trigger event for the entire policy.
Concept: State
-##############
-
- .. container:: paragraph
-
- The *State* concept represents a phase or a stage in a
- policy, with a policy being composed of a series of
- states. Each state has at least one but may have many
- tasks and, on each run of execution, a state executes
- one and only one of its tasks. If a state has more
- than one task, then its task selection logic is used
- to select which task to execute. Task selection logic
- is programmable logic provided by the state designer.
- That logic can use incoming, policy, global, and
- external context to select which task best
- accomplishes the purpose of the state in a give
- situation if more than one task has been specified on
- a state. A state calls one and only one task when it
- is executed.
-
- .. container:: paragraph
-
- Each state is triggered by an event, which means that
- all tasks of a state must also be triggered by that
- same event. The set of output events for a state is
- the union of all output events from all tasks for that
- task. In practice at the moment, because a state can
- only have a single input event, a state that is not
- the final state of a policy may only output a single
- event and all tasks of that state may also only output
- that single event. In future work, the concept of
- having a less restrictive trigger pattern will be
- examined.
-
- .. container:: paragraph
-
- A *State* concept is keyed with a ``ReferenceKey``
- key, which references the *Policy* concept that owns
- the state. The *LocalName* field of the
- ``ReferenceKey`` holds the name of the state. As a
- state is part of a chain of states, the *NextState*
- field of a state holds the ``ReferenceKey`` key of the
- state in the policy to execute after this state.
-
- .. container:: paragraph
-
- The *Trigger* field of a state holds the
- ``ArtifactKey`` of the event that triggers this state.
- The *OutgoingEvents* field holds the ``ArtifactKey``
- references of all possible events that may be output
- from the state. This is a set that is the union of all
- output events of all tasks of the state.
-
- .. container:: paragraph
-
- The *Task* concepts that hold the definitions of the
- task for the state are held as a set of
- ``ArtifactKey`` references in the state. The
- *DefaultTask* field holds a reference to the default
- task for the state, a task that is executed if no task
- selection logic is specified. If the state has only
- one task, that task is the default task.
-
- .. container:: paragraph
-
- The *Logic* concept referenced by a state holds the
- task selection logic for a state. The task selection
- logic uses the incoming context (parameters of the
- incoming event) and other context to determine the
- best task to use to execute its goals. The state holds
- a set of references to *ContextItem* and
- *ContextItemTemplate* definitions for the context used
- by its task selection logic.
+==============
+
+.. container:: paragraph
+
+ The *State* concept represents a phase or a stage in a policy, with a policy being composed of a series of states.
+ Each state has at least one but may have many tasks and, on each run of execution, a state executes one and only one
+ of its tasks. If a state has more than one task, then its task selection logic is used to select which task to
+ execute. Task selection logic is programmable logic provided by the state designer. That logic can use incoming,
+ policy, global, and external context to select which task best accomplishes the purpose of the state in a give
+ situation if more than one task has been specified on a state. A state calls one and only one task when it is
+ executed.
+
+.. container:: paragraph
+
+ Each state is triggered by an event, which means that all tasks of a state must also be triggered by that same
+ event. The set of output events for a state is the union of all output events from all tasks for that task. In
+ practice at the moment, because a state can only have a single input event, a state that is not the final state of a
+ policy may only output a single event and all tasks of that state may also only output that single event. In future
+ work, the concept of having a less restrictive trigger pattern will be examined.
+
+.. container:: paragraph
+
+ A *State* concept is keyed with a ``ReferenceKey`` key, which references the *Policy* concept that owns the state.
+ The *LocalName* field of the ``ReferenceKey`` holds the name of the state. As a state is part of a chain of states,
+ the *NextState* field of a state holds the ``ReferenceKey`` key of the state in the policy to execute after this
+ state.
+
+.. container:: paragraph
+
+ The *Trigger* field of a state holds the ``ArtifactKey`` of the event that triggers this state. The *OutgoingEvents*
+ field holds the ``ArtifactKey`` references of all possible events that may be output from the state. This is a set
+ that is the union of all output events of all tasks of the state.
+
+.. container:: paragraph
+ The *Task* concepts that hold the definitions of the task for the state are held as a set of ``ArtifactKey``
+ references in the state. The *DefaultTask* field holds a reference to the default task for the state, a task that is
+ executed if no task selection logic is specified. If the state has only one task, that task is the default task.
+
+.. container:: paragraph
+
+ The *Logic* concept referenced by a state holds the task selection logic for a state. The task selection logic uses
+ the incoming context (parameters of the incoming event) and other context to determine the best task to use to
+ execute its goals. The state holds a set of references to *ContextItem* and *ContextItemTemplate* definitions for the
+ context used by its task selection logic.
+
+*************
Writing Logic
-^^^^^^^^^^^^^
+*************
Writing APEX Task Logic
------------------------
-
- .. container:: paragraph
-
- Task logic specifies the behavior of an Apex 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 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 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*. 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.
- * ================================================================================
- * 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:: 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.
- * ================================================================================
- * 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. 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 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).
-
- .. 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> | .. container:: paragraph |
-| | | | |
-| | | | The incoming task fields. 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); |
-+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
+=======================
-Writing APEX Task Selection Logic
----------------------------------
+.. container:: paragraph
+
+ Task logic specifies the behavior of an Apex 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 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
-
- 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"); |
-+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
-| parameters | Fields | java.util.Map <String,String> | .. container:: paragraph |
-| | | | |
-| | | | All parameters in the current task. This is implemented as a standard Java Map. |
-| | | | |
-| | | | .. container:: |
-| | | | |
-| | | | .. container:: content |
-| | | | |
-| | | | .. container:: paragraph |
-| | | | |
-| | | | **Example:** |
-| | | | |
-| | | | .. code:: javascript |
-| | | | |
-| | | | executor.parameters.get("ParameterKey1")) |
-+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
-| 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.
+.. 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*. 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 |
+ | | | | |
+ | | | |**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.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 ) |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.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"); |
+ +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+
+ | 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.
Finish Logic with Success or Error
-##################################
+----------------------------------
- .. container:: paragraph
+.. container:: paragraph
- To finish logic, i.e. return to APEX, with success use the
- following line close to the end of the logic.
+ To finish logic, i.e. return to APEX, with success use the following line close to the end of the logic.
- .. container:: listingblock
+.. container:: listingblock
- .. container:: title
+ .. container:: title
- JS Success
+ JS Success
- .. container:: content
+ .. container:: content
- .. code:: javascript
+ .. code:: javascript
- true;
+ true;
- .. container:: paragraph
+.. container:: paragraph
- To notify a problem, finish with an error.
+ To notify a problem, finish with an error.
- .. container:: listingblock
+ .. container:: listingblock
- .. container:: title
+ .. container:: title
- JS Fail
+ JS Fail
- .. container:: content
+ .. container:: content
- .. code:: javascript
+ .. code:: javascript
- false;
+ false;
Logic Logging
-#############
+-------------
- .. container:: paragraph
+.. container:: paragraph
- Logging can be made easy using a local variable for the logger.
- Line 1 below does that. Then we start with a trace log with the
- task (or task logic) identifier followed by the infields.
+ Logging can be made easy using a local variable for the logger. Line 1 below does that. Then we start
+ with a trace log with the task (or task logic) identifier followed by the infields.
- .. container:: listingblock
+.. container:: listingblock
- .. container:: title
+ .. container:: title
- JS Logging
+ JS Logging
- .. container:: content
+ .. container:: content
- .. code:: javascript
+ .. code:: javascript
- var logger = executor.logger;
- logger.trace("start: " + executor.subject.id);
- logger.trace("-- infields: " + executor.inFields);
+ var logger = executor.logger;
+ logger.trace("start: " + executor.subject.id);
+ logger.trace("-- infields: " + executor.inFields);
- .. container:: paragraph
+.. container:: paragraph
- For larger logging blocks you can use the standard logging API
- to detect log levels, for instance:
+ For larger logging blocks you can use the standard logging API to detect log levels, for instance:
- .. container:: listingblock
+ .. container:: listingblock
- .. container:: title
+ .. container:: title
- JS Logging Blocks
+ JS Logging Blocks
- .. container:: content
+ .. container:: content
- .. code:: javascript
+ .. code:: javascript
- if(logger.isTraceEnabled()){
- // trace logging block here
- }
+ if(logger.isTraceEnabled()){
+ // trace logging block here
+ }
- .. container:: paragraph
+.. container:: paragraph
- Note: the shown logger here logs to
- ``org.onap.policy.apex.executionlogging``. The behavior of the
- actual logging can be specified in the
- ``$APEX_HOME/etc/logback.xml``.
+ Note: the shown logger here logs to ``org.onap.policy.apex.executionlogging``. The behavior of the actual logging can
+ be specified in the ``$APEX_HOME/etc/logback.xml``.
- .. container:: paragraph
+.. container:: paragraph
- If you want to log into the APEX root logger (which is
- sometimes necessary to report serious logic errors to the top),
- then import the required class and use this logger.
+ If you want to log into the APEX root logger (which is sometimes necessary to report serious logic errors to the top),
+ then import the required class and use this logger.
- .. container:: listingblock
+.. container:: listingblock
- .. container:: title
+ .. container:: title
- JS Root Logger
+ JS Root Logger
- .. container:: content
+ .. container:: content
- .. code:: javascript
+ .. code:: javascript
- importClass(org.slf4j.LoggerFactory);
- var rootLogger = LoggerFactory.getLogger(logger.ROOT_LOGGER_NAME);
-
- rootLogger.error("Serious error in logic detected: " + executor.subject.id);
+ var rootLogger = LoggerFactory.getLogger(logger.ROOT_LOGGER_NAME);
+ rootLogger.error("Serious error in logic detected: " + executor.subject.id);
Accessing TaskParameters
-########################
+------------------------
- .. container:: paragraph
+.. container:: paragraph
- TaskParameters available in a Task can be accessed in the logic.
- The parameters in each task are made available at the executor level.
- This example assumes a parameter with key ``ParameterKey1``.
+ TaskParameters available in a Task can be accessed in the logic. The parameters in each task are made
+ available at the executor level. This example assumes a parameter with key ``ParameterKey1``.
- .. container:: listingblock
+ .. container:: listingblock
- .. container:: title
+ .. container:: title
- JS TaskParameter value
+ JS TaskParameter value
- .. container:: content
+ .. container:: content
- .. code:: javascript
+ .. code:: javascript
- executor.parameters.get("ParameterKey1"))
+ executor.parameters.get("ParameterKey1"))
- .. container:: paragraph
+.. container:: paragraph
- Alternatively, the task parameters can also be accessed from the task object.
+ Alternatively, the task parameters can also be accessed from the task object.
- .. container:: listingblock
+ .. container:: listingblock
- .. container:: title
+ .. container:: title
- JS TaskParameter value using task object
+ JS TaskParameter value using task object
- .. container:: content
+ .. container:: content
- .. code:: javascript
+ .. code:: javascript
- executor.subject.task.getTaskParameters.get("ParameterKey1").getTaskParameterValue()
+ executor.subject.task.getTaskParameters.get("ParameterKey1").getTaskParameterValue()
Local Variable for Infields
-###########################
+---------------------------
- .. container:: paragraph
+.. container:: paragraph
- It is a good idea to use local variables for ``infields``. This
- avoids long code lines and policy evolution. The following
- example assumes infields named ``nodeName`` and ``nodeAlias``.
+ It is a good idea to use local variables for ``infields``. This avoids long code lines and policy
+ evolution. The following example assumes infields named ``nodeName`` and ``nodeAlias``.
- .. container:: listingblock
+ .. container:: listingblock
- .. container:: title
+ .. container:: title
- JS Infields Local Var
+ JS Infields Local Var
- .. container:: content
+ .. container:: content
- .. code:: javascript
+ .. code:: javascript
- var ifNodeName = executor.inFields["nodeName"];
- var ifNodeAlias = executor.inFields["nodeAlias"];
+ var ifNodeName = executor.inFields["nodeName"];
+ var ifNodeAlias = executor.inFields["nodeAlias"];
Local Variable for Context Albums
-#################################
+---------------------------------
- .. container:: paragraph
+.. container:: paragraph
- Similar to the ``infields`` it is good practice to use local
- variables for context albums as well. The following example
- assumes that a task can access a context album
- ``albumTopoNodes``. The second line gets a particular node from
- this context album.
+ Similar to the ``infields`` it is good practice to use local variables for context albums as well. The
+ following example assumes that a task can access a context album ``albumTopoNodes``. The second line gets a
+ particular node from this context album.
- .. container:: listingblock
+.. container:: listingblock
- .. container:: title
+ .. container:: title
- JS Infields Local Var
+ JS Infields Local Var
- .. container:: content
+ .. container:: content
- .. code:: javascript
+ .. code:: javascript
- var albumTopoNodes = executor.getContextAlbum("albumTopoNodes");
- var ctxtNode = albumTopoNodes.get(ifNodeName);
+ var albumTopoNodes = executor.getContextAlbum("albumTopoNodes");
+ var ctxtNode = albumTopoNodes.get(ifNodeName);
Set Outfields in Logic
-######################
+----------------------
- .. container:: paragraph
+.. container:: paragraph
- The task logic needs to set outfields with content generated.
- The exception are outfields that are a direct copy from an
- infield of the same name, APEX does that autmatically.
+ The task logic needs to set outfields with content generated. The exception are outfields that are a
+ direct copy from an infield of the same name, APEX does that autmatically.
- .. container:: listingblock
+.. container:: listingblock
- .. container:: title
+ .. container:: title
- JS Set Outfields
+ JS Set Outfields
- .. container:: content
+ .. container:: content
- .. code:: javascript
+ .. code:: javascript
- executor.outFields["report"] = "node ctxt :: added node " + ifNodeName;
+ executor.outFields["report"] = "node ctxt :: added node " + ifNodeName;
Create a instance of an Outfield using Schemas
-##############################################
+----------------------------------------------
+
+.. container:: paragraph
- .. container:: paragraph
+ If an outfield is not an atomic type (string, integer, etc.) but uses a complex schema (with a Java or
+ Avro backend), APEX can help to create new instances. The ``executor`` provides a field called ``subject``,
+ which provides a schem helper with an API for this. The complete API of the schema helper is documented here:
+ `API Doc: SchemaHelper <https://ericsson.github.io/apex-docs/javadocs/index.html>`__.
- If an outfield is not an atomic type (string, integer, etc.)
- but uses a complex schema (with a Java or Avro backend), APEX
- can help to create new instances. The ``executor`` provides a
- field called ``subject``, which provides a schem helper with an
- API for this. The complete API of the schema helper is
- documented here: `API Doc:
- SchemaHelper <https://ericsson.github.io/apex-docs/javadocs/index.html>`__.
+.. container:: paragraph
- .. container:: paragraph
+ If the backend is Java, then the Java class implementing the schema needs to be imported.
- If the backend is Avro, then an import of the Avro schema
- library is required:
+.. container:: paragraph
- .. container:: listingblock
+ The following example assumes an outfield ``situation``. The ``subject`` method ``getOutFieldSchemaHelper()`` is used
+ to create a new instance.
- .. container:: title
+.. container:: listingblock
- JS Import Avro
+ .. container:: title
- .. container:: content
+ JS Outfield Instance with Schema
- .. code:: javascript
+ .. container:: content
- importClass(org.apache.avro.generic.GenericData.Array);
- importClass(org.apache.avro.generic.GenericRecord);
- importClass(org.apache.avro.Schema);
+ .. code:: javascript
- .. container:: paragraph
+ var situation = executor.subject.getOutFieldSchemaHelper("situation").createNewInstance();
- If the backend is Java, then the Java class implementing the
- schema needs to be imported.
+.. container:: paragraph
- .. container:: paragraph
+ If the schema backend is Java, the new instance will be as implemented in the Java class. If the schema backend is
+ Avro, the new instance will have all fields from the Avro schema specification, but set to ``null``. So any entry here
+ needs to be done separately. For instance, the ``situation`` schema has a field ``problemID`` which we set.
- The following example assumes an outfield ``situation``. The
- ``subject`` method ``getOutFieldSchemaHelper()`` is used to
- create a new instance.
+.. container:: listingblock
- .. container:: listingblock
+ .. container:: title
- .. container:: title
+ JS Outfield Instance with Schema, set
- JS Outfield Instance with Schema
+ .. container:: content
- .. container:: content
+ .. code:: javascript
- .. code:: javascript
+ situation.put("problemID", "my-problem");
- var situation = executor.subject.getOutFieldSchemaHelper("situation").createNewInstance();
+Create a instance of an Context Album entry using Schemas
+---------------------------------------------------------
- .. container:: paragraph
+.. container:: paragraph
- If the schema backend is Java, the new instance will be as
- implemented in the Java class. If the schema backend is Avro,
- the new instance will have all fields from the Avro schema
- specification, but set to ``null``. So any entry here needs to
- be done separately. For instance, the ``situation`` schema has
- a field ``problemID`` which we set.
+ Context album instances can be created using very similar to the outfields. Here, the schema helper
+ comes from the context album directly. The API of the schema helper is the same as for outfields, see
+ `API Doc: SchemaHelper <https://ericsson.github.io/apex-docs/javadocs/index.html>`__.
- .. container:: listingblock
+.. container:: paragraph
- .. container:: title
+ If the backend is Java, then the Java class implementing the schema needs to be imported.
- JS Outfield Instance with Schema, set
+.. container:: paragraph
- .. container:: content
+ The following example creates a new instance of a context album instance named ``albumProblemMap``.
- .. code:: javascript
+.. container:: listingblock
- situation.put("problemID", "my-problem");
+ .. container:: title
-Create a instance of an Context Album entry using Schemas
-#########################################################
+ JS Outfield Instance with Schema
+
+ .. container:: content
- .. container:: paragraph
+ .. code:: javascript
- Context album instances can be created using very similar to
- the outfields. Here, the schema helper comes from the context
- album directly. The API of the schema helper is the same as for
- outfields, see `API Doc:
- SchemaHelper <https://ericsson.github.io/apex-docs/javadocs/index.html>`__.
+ var albumProblemMap = executor.getContextAlbum("albumProblemMap");
+ var linkProblem = albumProblemMap.getSchemaHelper().createNewInstance();
- .. container:: paragraph
+.. container:: paragraph
- If the backend is Avro, then an import of the Avro schema
- library is required:
+ This can of course be also done in a single call without the local variable for the context album.
- .. container:: listingblock
+.. container:: listingblock
- .. container:: title
+ .. container:: title
- JS Import Avro
+ JS Outfield Instance with Schema, one line
- .. container:: content
+ .. container:: content
- .. code:: javascript
+ .. code:: javascript
- importClass(org.apache.avro.generic.GenericData.Array);
- importClass(org.apache.avro.generic.GenericRecord);
- importClass(org.apache.avro.Schema);
+ var linkProblem = executor.getContextAlbum("albumProblemMap").getSchemaHelper().createNewInstance();
- .. container:: paragraph
+.. container:: paragraph
- If the backend is Java, then the Java class implementing the
- schema needs to be imported.
+ If the schema backend is Java, the new instance will be as implemented in the Java class. If the schema backend is
+ Avro, the new instance will have all fields from the Avro schema specification, but set to ``null``. So any entry here
+ needs to be done separately (see above in outfields for an example).
- .. container:: paragraph
+Enumerates
+----------
- The following example creates a new instance of a context album
- instance named ``albumProblemMap``.
+.. container:: paragraph
- .. container:: listingblock
+ When dealing with enumerates (Avro or Java defined), it is sometimes and in some execution
+ environments necessary to convert them to a string. For example, assume an Avro enumerate schema as:
- .. container:: title
+.. container:: listingblock
- JS Outfield Instance with Schema
+ .. container:: title
- .. container:: content
+ Avro Enumerate Schema
- .. code:: javascript
+ .. container:: content
- var albumProblemMap = executor.getContextAlbum("albumProblemMap");
- var linkProblem = albumProblemMap.getSchemaHelper().createNewInstance();
+ .. code:: javascript
- .. container:: paragraph
+ {
+ "type": "enum", "name": "Status", "symbols" : [
+ "UP", "DOWN"
+ ]
+ }
- This can of course be also done in a single call without the
- local variable for the context album.
+.. container:: paragraph
- .. container:: listingblock
+ Using a switch over a field initialized with this enumerate in Javascript will fail. Instead, use the ``toString`` method, for example:
- .. container:: title
+.. container:: listingblock
- JS Outfield Instance with Schema, one line
+ .. container:: title
- .. container:: content
+ JS Outfield Instance with Schema, one line
- .. code:: javascript
+ .. container:: content
- var linkProblem = executor.getContextAlbum("albumProblemMap").getSchemaHelper().createNewInstance();
+ .. code:: javascript
- .. container:: paragraph
+ var switchTest = executor.inFields["status"]; switch(switchTest.toString()){
+ case "UP": ...; break; case "DOWN": ...; break; default: ...;
+ }
- If the schema backend is Java, the new instance will be as
- implemented in the Java class. If the schema backend is Avro,
- the new instance will have all fields from the Avro schema
- specification, but set to ``null``. So any entry here needs to
- be done separately (see above in outfields for an example).
+MVEL Initialize Outfields First!
+--------------------------------
-Enumerates
-##########
+.. container:: paragraph
- .. container:: paragraph
+ In MVEL, we observed a problem when accessing (setting) outfields without a prior access to them. So
+ in any MVEL task logic, before setting any outfield, simply do a get (with any string), to load the outfields
+ into the MVEL cache.
- When dealing with enumerates (Avro or Java defined), it is
- sometimes and in some execution environments necessary to
- convert them to a string. For example, assume an Avro enumerate
- schema as:
+.. container:: listingblock
- .. container:: listingblock
+ .. container:: title
- .. container:: title
+ MVEL Outfield Initialization
- Avro Enumerate Schema
+ .. container:: content
- .. container:: content
+ .. code:: javascript
- .. code:: javascript
+ outFields.get("initialize outfields");
- {
- "type": "enum",
- "name": "Status",
- "symbols" : [
- "UP",
- "DOWN"
- ]
- }
+Using Java in Scripting Logic
+-----------------------------
- .. container:: paragraph
+.. container:: paragraph
- Using a switch over a field initialized with this enumerate in
- Javascript will fail. Instead, use the ``toString`` method, for
- example:
+ Since APEX executes the logic inside a JVM, most scripting languages provide access to all standard
+ Java classes. Simply add an import for the required class and then use it as in actual Java.
- .. container:: listingblock
+.. container:: paragraph
- .. container:: title
+ The following example imports ``java.util.arraylist`` into a Javascript logic, and then creates a new
+ list.
- JS Outfield Instance with Schema, one line
+.. container:: listingblock
- .. container:: content
+ .. container:: title
- .. code:: javascript
+ JS Import ArrayList
- var switchTest = executor.inFields["status"];
- switch(switchTest.toString()){
- case "UP": ...; break;
- case "DOWN": ...; break;
- default: ...;
- }
+ .. container:: content
-MVEL Initialize Outfields First!
-################################
+ .. code:: javascript
+
+ var myList = new ArrayList();
- .. container:: paragraph
+Converting Javascript scripts from Nashorn to Rhino dialects
+------------------------------------------------------------
- In MVEL, we observed a problem when accessing (setting)
- outfields without a prior access to them. So in any MVEL task
- logic, before setting any outfield, simply do a get (with any
- string), to load the outfields into the MVEL cache.
+The Nashorn Javascript engine was removed from Java in the Java 11 release. Java 11 was introduced into
+the Policy Framework in the Frankfurt release, so from Frankfurt on, APEX Javascript scripts use the Rhino
+Javascript engine and scripts must be in the Rhino dialect.
- .. container:: listingblock
+There are some minor but important differences between the dialects that users should be aware of so
+that they can convert their scripts into the Rhino dialect.
- .. container:: title
+Return Values
+^^^^^^^^^^^^^
- MVEL Outfield Initialization
+APEX scripts must always return a value of ``true`` indicating that the script executed correctly or ``false``
+indicating that there was an error in script execution.
- .. container:: content
+*Pre Frankfurt*
- .. code:: javascript
+In Nashorn dialect scripts, the user had to create a special variable called ``returnValue`` and set the value of
+that variable to be the return value for the script.
- outFields.get("initialize outfields");
+*Frankfurt and Later*
-Using Java in Scripting Logic
-#############################
+In Rhino dialect scripts, the return value of the script is the logical result of the last statement. Therefore the
+last line of the script must evaluate to either ``true`` or ``false``.
+
+.. container:: listingblock
+
+ .. container:: title
+
+ JS Rhino script last executed line examples
+
+ .. container:: content
+
+ .. code:: javascript
+
+ true;
+
+ returnValue; // Where returnValue is assigned earlier in the script
+
+ someValue == 1; // Where the value of someValue is assigned earlier in the script
+
+return statement
+^^^^^^^^^^^^^^^^
+
+The ``return`` statement is not supported from the main script called in the Rhino interpreter.
+
+*Pre Frankfurt*
+
+In Nashorn dialect scripts, the user could return a value of ``true`` or ``false`` at any point in their script.
+
+.. container:: listingblock
+
+ .. container:: title
+
+ JS Nashorn main script returning ``true`` and ``false``
+
+ .. container:: content
+
+ .. code:: javascript
+
+ var n;
+
+ // some code assigns n a value
+
+ if (n < 2) {
+ return false;
+ } else {
+ return true;
+ }
+
+*Frankfurt and Later*
+
+In Rhino dialect scripts, the ``return`` statement cannot be used in the main method, but it can still be used in
+functions. If you want to have a ``return`` statement in your code prior to the last statement, encapsulate your code
+in a function.
+
+.. container:: listingblock
+
+ .. container:: title
+
+ JS Rhino script with ``return`` statements in a function
+
+ .. container:: content
+
+ .. code:: javascript
+
+ someFunction();
+
+ function someFunction() {
+ var n;
+
+ // some code assigns n a value
+
+ if (n < 2) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+Compatibility Script
+^^^^^^^^^^^^^^^^^^^^
+
+For Nashorn, the user had to call a compatibility script at the beginning of their Javascript script. This is not
+required in Rhino.
+
+*Pre Frankfurt*
+
+In Nashorn dialect scripts, the compatibility script must be loaded.
+
+.. container:: listingblock
+
+ .. container:: title
+
+ Nashorn compatability script loading
+
+ .. container:: content
+
+ .. code:: javascript
+
+ load("nashorn:mozilla_compat.js");
+
+*Frankfurt and Later*
+
+Not required.
+
+Import of Java classes
+^^^^^^^^^^^^^^^^^^^^^^
+
+For Nashorn, the user had explicitly import all the Java packages and classes they wished to use in their Javascript
+script. In Rhino, all Java classes on the classpath are available for use.
+
+*Pre Frankfurt*
+
+In Nashorn dialect scripts, Java classes must be imported.
+
+.. container:: listingblock
+
+ .. container:: title
+
+ Importation of Java packages and classes
+
+ .. container:: content
+
+ .. code:: javascript
+
+ importPackage(java.text);
+ importClass(java.text.SimpleDateFormat);
+
+*Frankfurt and Later*
+
+Not required.
+
+Using Java Classes and Objects as Variables
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Setting a Javascript variable to hold a Java class or a Java object is more straightforward in Rhino than it is in
+Nashorn. The examples below show how to instantiate a Javascript variable as a Java class and how to use that variable
+to create an instance of the Java class in another Javascript variable in both dialects.
+
+
+*Pre Frankfurt*
+
+.. container:: listingblock
- .. container:: paragraph
+ .. container:: title
- Since APEX executes the logic inside a JVM, most scripting
- languages provide access to all standard Java classes. Simply
- add an import for the required class and then use it as in
- actual Java.
+ Create Javascript variables to hold a Java class and instance
- .. container:: paragraph
+ .. container:: content
- The following example imports ``java.util.arraylist`` into a
- Javascript logic, and then creates a new list.
+ .. code:: javascript
- .. container:: listingblock
+ var webClientClass = Java.type("org.onap.policy.apex.examples.bbs.WebClient");
+ var webClientObject = new webClientClass();
- .. container:: title
+*Frankfurt and Later*
- JS Import ArrayList
+.. container:: listingblock
- .. container:: content
+ .. container:: title
- .. code:: javascript
+ Create Javascript variables to hold a Java class and instance
- importClass(java.util.ArrayList);
- var myList = new ArrayList();
+ .. container:: content
+ .. code:: javascript
-.. container::
- :name: footer
+ var webClientClass = org.onap.policy.apex.examples.bbs.WebClient;
+ var webClientObject = new webClientClass();
- .. container::
- :name: footer-text
+Equal Value and Equal Type operator ``===``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- 2.3.0-SNAPSHOT
- Last updated 2020-03-16 16:04:24 GMT
+The *Equal Value and Equal Type* operator ``===`` is not supported in Rhino. Developers must use the Equal To
+operator ``==`` instead. To check types, they may need to explicitly find and check the type of the variables
+they are using.
.. |APEX Policy Matrix| image:: images/apex-intro/ApexPolicyMatrix.png
.. |APEX Policy Model for Execution| image:: images/apex-policy-model/UmlPolicyModels.png