aboutsummaryrefslogtreecommitdiffstats
path: root/docs/APEX-Policy-Guide.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/APEX-Policy-Guide.rst')
-rw-r--r--docs/APEX-Policy-Guide.rst2133
1 files changed, 2133 insertions, 0 deletions
diff --git a/docs/APEX-Policy-Guide.rst b/docs/APEX-Policy-Guide.rst
new file mode 100644
index 000000000..392f31c7b
--- /dev/null
+++ b/docs/APEX-Policy-Guide.rst
@@ -0,0 +1,2133 @@
+.. 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
+
+ APEX offers a lot of flexibility for defining, deploying,
+ and executing policies. Based on a theoretic model, it
+ supports virtually any policy model and allows to
+ translate 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
+
+ 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:: content
+
+ |APEX Policy Matrix|
+
+ .. 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)
+
+ .. 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
+
+ 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)
+
+ - Intent (instead of providing detailed actions the
+ response is an intent statement and a further system
+ processes that)
+
+ - Delegation (hand the problem over to someone else,
+ possibly with some information or instructions)
+
+ - Fail / Error (the policy has encountered a problem,
+ and reports it)
+
+ - Feedback (why did the policy make a certain decision)
+
+APEX Policy Model
+^^^^^^^^^^^^^^^^^
+
+Introduction
+------------
+
+ .. 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 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.
+
+ - 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).
+
+ - 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.
+
+ - 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:: imageblock
+
+ .. container:: content
+
+ |APEX Policy Model for Execution|
+
+ .. container:: title
+
+ Figure 2. APEX Policy Model for Execution
+
+Concepts and Keys
+#################
+
+ .. 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:
+
+ .. container:: imageblock
+
+ .. container:: content
+
+ |Concepts and Keys|
+
+ .. container:: title
+
+ Figure 3. Concepts and Keys
+
+ .. container:: ulist
+
+ - ``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
+
+ - ``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
+
+ - ``equals()`` - checks if two instances of this
+ concept are equal
+
+ - ``toString()`` - returns a string representation of
+ 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.
+
+ .. 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*.
+
+ .. 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.
+
+ .. 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.
+
+ .. 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``.
+
+ .. 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/>`__.
+
+Concept: PolicyModel
+####################
+
+ .. 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.
+
+ .. 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.
+
+Concept: DataType
+#################
+
+ .. 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.
+
+Concept: Event
+##############
+
+ .. 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:
+
+ .. container:: ulist
+
+ - The *NameSpace* identifies the domain of
+ application of 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
+
+ .. 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.
+
+Concept: EventField
+###################
+
+ .. 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.
+
+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.
+
+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.
+
+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.
+
+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.
+
+Concept: TaskParameter
+######################
+
+ .. 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.
+
+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.
+
+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.
+
+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.
+
+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
+
+ 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 -DskipTest |
+ | >mvn clean install -DskipTests | |
+ +-------------------------------------------------------+--------------------------------------------------------+
+
++------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
+| Name | Type | Java type | Description |
++============+=============+================================+=====================================================================================+
+| inFields | Fields | java.util.Map <String,Object> | .. container:: paragraph |
+| | | | |
+| | | | All fields in the state’s incoming event. This is implemented as a standard Java |
+| | | | Java (unmodifiable) Map |
+| | | | |
+| | | | .. container:: |
+| | | | |
+| | | | .. container:: content |
+| | | | |
+| | | | .. container:: paragraph |
+| | | | |
+| | | | **Example:** |
+| | | | |
+| | | | .. code:: javascript |
+| | | | |
+| | | | executor.logger.debug("Incoming fields: " |
+| | | | +executor.inFields.entrySet()); |
+| | | | var item_id = executor.incomingFields["item_ID"]; |
+| | | | if (item_id >=1000) { ... } |
++------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
+| outFields | Fields | java.util.Map <String,Object> | .. container:: paragraph |
+| | | | |
+| | | | The outgoing task fields. This is implemented as a standard initially empty Java |
+| | | | (modifiable) Map. To create a new schema-compliant instance of a field object |
+| | | | see the utility method subject.getOutFieldSchemaHelper() below |
+| | | | |
+| | | | .. container:: |
+| | | | |
+| | | | .. container:: content |
+| | | | |
+| | | | .. container:: paragraph |
+| | | | |
+| | | | **Example:** |
+| | | | |
+| | | | .. code:: javascript |
+| | | | |
+| | | | executor.outFields["authorised"] = false; |
++------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
+| logger | Logger | org.slf4j.ext.XLogger | .. container:: paragraph |
+| | | | |
+| | | | A helpful logger |
+| | | | |
+| | | | .. container:: |
+| | | | |
+| | | | .. container:: content |
+| | | | |
+| | | | .. container:: paragraph |
+| | | | |
+| | | | **Example:** |
+| | | | |
+| | | | .. code:: javascript |
+| | | | |
+| | | | executor.logger.info("Executing task: " |
+| | | | +executor.subject.id); |
++------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
+| TRUE/FALSE | boolean | java.lang.Boolean | .. container:: paragraph |
+| | | | |
+| | | | 2 helpful constants. These are useful to retrieve correct return values for the |
+| | | | task logic |
+| | | | |
+| | | | .. container:: |
+| | | | |
+| | | | .. container:: content |
+| | | | |
+| | | | .. container:: paragraph |
+| | | | |
+| | | | **Example:** |
+| | | | |
+| | | | .. code:: javascript |
+| | | | |
+| | | | var returnValue = executor.isTrue; |
+| | | | var returnValueType = Java.type("java.lang.Boolean"); |
+| | | | var returnValue = new returnValueType(true); |
++------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
+| subject | Task | TaskFacade | .. container:: paragraph |
+| | | | |
+| | | | This provides some useful information about the task that contains this task |
+| | | | logic. This object has some useful fields and methods : |
+| | | | |
+| | | | .. container:: ulist |
+| | | | |
+| | | | - **AxTask task** to get access to the full task definition of |
+| | | | the host task |
+| | | | |
+| | | | - **String getTaskName()** to get the name of the host task |
+| | | | |
+| | | | - **String getId()** to get the ID of the host task |
+| | | | |
+| | | | - **SchemaHelper getInFieldSchemaHelper( String fieldName )** to |
+| | | | get a ``SchemaHelper`` helper object to manipulate incoming |
+| | | | task fields in a schema-aware manner |
+| | | | |
+| | | | - **SchemaHelper getOutFieldSchemaHelper( String fieldName )** to |
+| | | | get a ``SchemaHelper`` helper object to manipulate outgoing |
+| | | | task fields in a schema-aware manner, e.g. to instantiate new |
+| | | | schema-compliant field objects to populate the |
+| | | | ``executor.outFields`` outgoing fields map |
+| | | | |
+| | | | .. container:: |
+| | | | |
+| | | | .. container:: content |
+| | | | |
+| | | | .. container:: paragraph |
+| | | | |
+| | | | **Example:** |
+| | | | |
+| | | | .. code:: javascript |
+| | | | |
+| | | | executor.logger.info("Task name: " |
+| | | | +executor.subject.getTaskName()); |
+| | | | executor.logger.info("Task id: " |
+| | | | +executor.subject.getId()); |
+| | | | executor.logger.info("Task inputs definitions: " |
+| | | | +"executor.subject.task.getInputFieldSet()); |
+| | | | executor.logger.info("Task outputs definitions: " |
+| | | | +"executor.subject.task.getOutputFieldSet()); |
+| | | | executor.outFields["authorised"] = executor.subject |
+| | | | .getOutFieldSchemaHelper("authorised") |
+| | | | .createNewInstance("false"); |
++------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
+| ContextAlbum getContextAlbum(String ctxtAlbumName ) | .. container:: paragraph |
+| | |
+| | A utility method to retrieve a ``ContextAlbum`` for use in the task. |
+| | This is how you access the context used by the task. The returned |
+| | ``ContextAlbum`` implements the ``java.util.Map <String,Object>`` |
+| | interface to get and set context as appropriate. The returned |
+| | ``ContextAlbum`` also has methods to lock context albums, get |
+| | information about the schema of the items to be stored in a context |
+| | album, and get a ``SchemaHelper`` to manipulate context album items. How |
+| | to define and use context in a task is described in the Apex |
+| | Programmer’s Guide and in the My First Apex Policy guide. |
+| | |
+| | .. container:: |
+| | |
+| | .. container:: content |
+| | |
+| | .. container:: paragraph |
+| | |
+| | **Example:** |
+| | |
+| | .. code:: javascript |
+| | |
+| | var bkey = executor.inFields.get("branch_ID"); |
+| | var cnts = executor.getContextMap("BranchCounts"); |
+| | cnts.lockForWriting(bkey); |
+| | cnts.put(bkey, cnts.get(bkey) + 1); |
+| | cnts.unlockForWriting(bkey); |
++------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+
+
+Logic Cheatsheet
+----------------
+
+ .. container:: paragraph
+
+ Examples given here use Javascript (if not stated otherwise),
+ other execution environments will be similar.
+
+Add Nashorn
+###########
+
+ .. container:: paragraph
+
+ First line in the logic use this import.
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Nashorn
+
+ .. container:: content
+
+ .. code:: javascript
+
+ load("nashorn:mozilla_compat.js");
+
+Finish Logic with Success or Error
+##################################
+
+ .. container:: paragraph
+
+ To finish logic, i.e. return to APEX, with success use the
+ following lines close to the end of the logic.
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Success
+
+ .. container:: content
+
+ .. code:: javascript
+
+ var returnValueType = Java.type("java.lang.Boolean");
+ var returnValue = new returnValueType(true);
+
+ .. container:: paragraph
+
+ To notify a problem, finish with an error.
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Fail
+
+ .. container:: content
+
+ .. code:: javascript
+
+ var returnValueType = Java.type("java.lang.Boolean");
+ var returnValue = new returnValueType(false);
+
+Logic Logging
+#############
+
+ .. 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.
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Logging
+
+ .. container:: content
+
+ .. code:: javascript
+
+ var logger = executor.logger;
+ logger.trace("start: " + executor.subject.id);
+ logger.trace("-- infields: " + executor.inFields);
+
+ .. container:: paragraph
+
+ For larger logging blocks you can use the standard logging API
+ to detect log levels, for instance:
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Logging Blocks
+
+ .. container:: content
+
+ .. code:: javascript
+
+ if(logger.isTraceEnabled()){
+ // trace logging block here
+ }
+
+ .. 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``.
+
+ .. 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.
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Root Logger
+
+ .. container:: content
+
+ .. code:: javascript
+
+ importClass(org.slf4j.LoggerFactory);
+ var rootLogger = LoggerFactory.getLogger(logger.ROOT_LOGGER_NAME);
+
+ rootLogger.error("Serious error in logic detected: " + executor.subject.id);
+
+Local Variable for Infields
+###########################
+
+ .. 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``.
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Infields Local Var
+
+ .. container:: content
+
+ .. code:: javascript
+
+ var ifNodeName = executor.inFields["nodeName"];
+ var ifNodeAlias = executor.inFields["nodeAlias"];
+
+Local Variable for Context Albums
+#################################
+
+ .. 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.
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Infields Local Var
+
+ .. container:: content
+
+ .. code:: javascript
+
+ var albumTopoNodes = executor.getContextAlbum("albumTopoNodes");
+ var ctxtNode = albumTopoNodes.get(ifNodeName);
+
+Set Outfields in Logic
+######################
+
+ .. 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.
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Set Outfields
+
+ .. container:: content
+
+ .. code:: javascript
+
+ executor.outFields["report"] = "node ctxt :: added node " + ifNodeName;
+
+Create a instance of an Outfield using Schemas
+##############################################
+
+ .. 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>`__.
+
+ .. container:: paragraph
+
+ If the backend is Avro, then an import of the Avro schema
+ library is required:
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Import Avro
+
+ .. container:: content
+
+ .. code:: javascript
+
+ importClass(org.apache.avro.generic.GenericData.Array);
+ importClass(org.apache.avro.generic.GenericRecord);
+ importClass(org.apache.avro.Schema);
+
+ .. container:: paragraph
+
+ If the backend is Java, then the Java class implementing the
+ schema needs to be imported.
+
+ .. container:: paragraph
+
+ The following example assumes an outfield ``situation``. The
+ ``subject`` method ``getOutFieldSchemaHelper()`` is used to
+ create a new instance.
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Outfield Instance with Schema
+
+ .. container:: content
+
+ .. code:: javascript
+
+ var situation = executor.subject.getOutFieldSchemaHelper("situation").createNewInstance();
+
+ .. 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.
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Outfield Instance with Schema, set
+
+ .. container:: content
+
+ .. code:: javascript
+
+ situation.put("problemID", "my-problem");
+
+Create a instance of an Context Album entry using Schemas
+#########################################################
+
+ .. container:: paragraph
+
+ 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:: paragraph
+
+ If the backend is Avro, then an import of the Avro schema
+ library is required:
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Import Avro
+
+ .. container:: content
+
+ .. code:: javascript
+
+ importClass(org.apache.avro.generic.GenericData.Array);
+ importClass(org.apache.avro.generic.GenericRecord);
+ importClass(org.apache.avro.Schema);
+
+ .. container:: paragraph
+
+ If the backend is Java, then the Java class implementing the
+ schema needs to be imported.
+
+ .. container:: paragraph
+
+ The following example creates a new instance of a context album
+ instance named ``albumProblemMap``.
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Outfield Instance with Schema
+
+ .. container:: content
+
+ .. code:: javascript
+
+ var albumProblemMap = executor.getContextAlbum("albumProblemMap");
+ var linkProblem = albumProblemMap.getSchemaHelper().createNewInstance();
+
+ .. container:: paragraph
+
+ This can of course be also done in a single call without the
+ local variable for the context album.
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Outfield Instance with Schema, one line
+
+ .. container:: content
+
+ .. code:: javascript
+
+ var linkProblem = executor.getContextAlbum("albumProblemMap").getSchemaHelper().createNewInstance();
+
+ .. 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 (see above in outfields for an example).
+
+Enumerates
+##########
+
+ .. container:: paragraph
+
+ 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:: title
+
+ Avro Enumerate Schema
+
+ .. container:: content
+
+ .. code:: javascript
+
+ {
+ "type": "enum",
+ "name": "Status",
+ "symbols" : [
+ "UP",
+ "DOWN"
+ ]
+ }
+
+ .. container:: paragraph
+
+ Using a switch over a field initialized with this enumerate in
+ Javascript will fail. Instead, use the ``toString`` method, for
+ example:
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Outfield Instance with Schema, one line
+
+ .. container:: content
+
+ .. code:: javascript
+
+ var switchTest = executor.inFields["status"];
+ switch(switchTest.toString()){
+ case "UP": ...; break;
+ case "DOWN": ...; break;
+ default: ...;
+ }
+
+MVEL Initialize Outfields First!
+################################
+
+ .. 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.
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ MVEL Outfield Initialization
+
+ .. container:: content
+
+ .. code:: javascript
+
+ outFields.get("initialize outfields");
+
+Using Java in Scripting Logic
+#############################
+
+ .. container:: paragraph
+
+ 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:: paragraph
+
+ The following example imports ``java.util.arraylist`` into a
+ Javascript logic, and then creates a new list.
+
+ .. container:: listingblock
+
+ .. container:: title
+
+ JS Import ArrayList
+
+ .. container:: content
+
+ .. code:: javascript
+
+ importClass(java.util.ArrayList);
+ var myList = new ArrayList();
+
+Policy Examples
+^^^^^^^^^^^^^^^
+
+My First Policy
+---------------
+
+ .. container:: paragraph
+
+ A good starting point is the ``My First Policy`` example. It
+ describes a sales problem, to which policy can be applied.
+ The example details the policy background, shows how to use
+ the REST Editor to create a policy, and provides details for
+ running the policies. The documentation can be found:
+
+ .. container:: ulist
+
+ - `My-First-Policy on the APEX
+ site <https://ericsson.github.io/apex-docs/modules/examples/examples-myfirstpolicy/MyFirstPolicyHowto.html>`__
+
+ - `Stand-alone
+ HTML <https://ericsson.github.io/apex-docs/docs-apex/html/HowTo-MyFirstPolicy.html>`__
+
+ - `Stand-alone
+ PDF <https://ericsson.github.io/apex-docs/docs-apex/pdf/HowTo-MyFirstPolicy.pdf>`__
+
+VPN SLA
+-------
+
+ .. container:: paragraph
+
+ The domain Policy-controlled Video Streaming (PCVS) contains
+ a policy for controlling video streams with different
+ strategies. It also provides details for installing an
+ actual testbed with off-the-shelve software (Mininet,
+ Floodlight, Kafka, Zookeeper). The policy model here
+ demonstrates virtually all APEX features: local context and
+ policies controlling it, task selection logic and multiple
+ tasks in a single state, AVRO schemas for context, AVOR
+ schemas for events (trigger and local), and a CLI editor
+ specification of the policy. The documentation can be found:
+
+ .. container:: ulist
+
+ - `VPN SLA Policy on the APEX
+ site <https://ericsson.github.io/apex-docs/modules/examples/examples-pcvs/vpnsla/policy.html>`__
+
+Decision Maker
+--------------
+
+ .. container:: paragraph
+
+ The domain Decision Maker shows a very simple policy for
+ decisions. Interesting here is that the it creates a Docker
+ image to run the policy and that it uses the APEX REST
+ applications to update the policy on the-fly. It also has
+ local context to remember past decisions, and shows how to
+ use that to no make the same decision twice in a row. The
+ documentation can be found:
+
+ .. container:: ulist
+
+ - `Decision Maker on APEX
+ site <https://ericsson.github.io/apex-docs/modules/examples/examples-decisionmaker/index.html>`__
+
+.. container::
+ :name: footer
+
+ .. container::
+ :name: footer-text
+
+ 2.0.0-SNAPSHOT
+ Last updated 2018-09-04 16:04:24 IST
+
+.. |APEX Policy Matrix| image:: images/apex-intro/ApexPolicyMatrix.png
+.. |APEX Policy Model for Execution| image:: images/apex-policy-model/UmlPolicyModels.png
+.. |Concepts and Keys| image:: images/apex-policy-model/ConceptsKeys.png
+