diff options
Diffstat (limited to 'docs/APEX-Policy-Guide.rst')
-rw-r--r-- | docs/APEX-Policy-Guide.rst | 2133 |
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 + |