From f398107e1423eb77a30f5fcc5402cf557a077170 Mon Sep 17 00:00:00 2001 From: liamfallon Date: Fri, 10 Jul 2020 15:59:25 +0100 Subject: apex policy guide: Frankfurt changes Changes introduced in Frankfurt are reflected in the document, especially how to convert Javascript scripts from Nashorn format to Rhino format. Issue-ID: POLICY-2686 Change-Id: I3661fbdd66c4f360c9013a3c7aab81df32133829 Signed-off-by: liamfallon --- docs/apex/APEX-Policy-Guide.rst | 3265 ++++++++++++++++++--------------------- 1 file changed, 1475 insertions(+), 1790 deletions(-) diff --git a/docs/apex/APEX-Policy-Guide.rst b/docs/apex/APEX-Policy-Guide.rst index cb2388e0..cb4ae729 100644 --- a/docs/apex/APEX-Policy-Guide.rst +++ b/docs/apex/APEX-Policy-Guide.rst @@ -1,2107 +1,1792 @@ -.. This work is licensed under a Creative Commons Attribution 4.0 International License. -.. http://creativecommons.org/licenses/by/4.0 - + .. This work is licensed under a Creative Commons Attribution 4.0 International License. + .. http://creativecommons.org/licenses/by/4.0 +################# APEX Policy Guide -***************************** +################# + .. contents:: :depth: 3 +****************** APEX Policy Matrix -^^^^^^^^^^^^^^^^^^ +****************** -APEX Policy Matrix ------------------- +.. container:: paragraph - .. container:: paragraph + APEX offers a lot of flexibility for defining, deploying, and executing policies. Based on a theoretic model, it + supports virtually any policy model and supports translation of legacy policies into the APEX execution format. + However, the most important aspect for using APEX is to decide what policy is needed, what underlying policy concepts + should be used, and how the decision logic should be realized. Once these aspects are decided, APEX can be used to + execute the policies. If the policy evolves, say from a simple decision table to a fully adaptable policy, only the + policy definition requires change. APEX supports all of that. - APEX offers a lot of flexibility for defining, deploying, - and executing policies. Based on a theoretic model, it - supports virtually any policy model and supports - translation of legacy policies into the APEX execution format. - However, the most important aspect for using APEX is to - decide what policy is needed, what underlying policy - concepts should be used, and how the decision logic - should be realized. Once these aspects are decided, APEX - can be used to execute the policies. If the policy - evolves, say from a simple decision table to a fully - adaptable policy, only the policy definition requires - change. APEX supports all of that. +.. container:: paragraph - .. container:: paragraph + The figure below shows a (non-exhaustive) matrix, which will help to decide what policy is required to solve your + problem. Read the matrix from left to right choosing one cell in each column. - The figure below shows a (non-exhaustive) matrix, which - will help to decide what policy is required to solve your - problem. Read the matrix from left to right choosing one - cell in each column. +.. container:: imageblock - .. container:: imageblock + .. container:: content - .. container:: content + |APEX Policy Matrix| - |APEX Policy Matrix| + .. container:: title - .. container:: title - - Figure 1. APEX Policy Matrix - - .. container:: paragraph - - The policy can support one of a number of stimuli with an - associated purpose/model of the policy, for instance: - - .. container:: ulist - - - Configuration, i.e. what should happen. An example is - an event that states an intended network configuration - and the policy should provide the detailed actions for - it. The policy can be realized for instance as an - obligation policy, a promise or an intent. - - - Report, i.e. something did happen. An example is an - event about an error or fault and the policy needs to - repair that problem. The policy would usually be an - obligation, utility function, or goal policy. - - - Monitoring, i.e. something does happen. An example is - a notification about certain network conditions, to - which the policy might (or might not) react. The - policy will mitigate the monitored events or permit - (deny) related actions as an obligation or - authorization. - - - Analysis, i.e. why did something happen. An example is - an analytic component sends insights of a situation - requiring a policy to act on it. The policy can solve - the problem, escalate it, or delegate it as a refrain - or delegation policy. - - - Prediction, i.e. what will happen next. An example are - events that a policy uses to predict a future network - condition. The policy can prevent or enforce the - prediction as an adaptive policy, a utility function, - or a goal. - - - Feedback, i.e. why did something happen or not happen. - Similar to analysis, but here the feedback will be in - the input event and the policy needs to something with - that information. Feedback can be related to history - or experience, for instance a previous policy - execution. The policy needs to be context-aware or be - a meta-policy. - - .. container:: paragraph - - Once the purpose of the policy is decided, the next step - is to look into what context information the policy will - require to do its job. This can range from very simple to - a lot of different information, for instance: - - .. container:: ulist - - - No context, nothing but a trigger event, e.g. a string - or a number, is required - - - Event context, the incoming event provides all - information (more than a string or number) for the - policy - - - Policy context (read only), the policy has access to - additional information related to its class but cannot - change/alter them - - - Policy context (read and write), the policy has access - to additional information related to its class and can - alter this information (for instance to record - historic information) - - - Global context (read only), the policy has access to - additional information of any kind but cannot - change/alter them - - - Global context (read and write), the policy the policy - has access to additional information of any kind and - can alter this information (for instance to record - historic information) + Figure 1. APEX Policy Matrix - .. container:: paragraph +.. container:: paragraph - The next step is to decide how the policy should do its - job, i.e. what flavor it has, how many states are needed, - and how many tasks. There are many possible combinations, - for instance: + The policy can support one of a number of stimuli with an associated purpose/model of the policy, for instance: - .. container:: ulist +.. container:: ulist - - Simple / God: a simple policy with 1 state and 1 task, - which is doing everything for the decision-making. - This is the ideal policy for simple situation, e.g. - deciding on configuration parameters or simple access - control. + - Configuration, i.e. what should happen. An example is an event that states an intended network configuration + and the policy should provide the detailed actions for it. The policy can be realized for instance as an + obligation policy, a promise or an intent. - - Simple sequence: a simple policy with a number of - states each having a single task. This is a very good - policy for simple decision-making with different - steps. For instance, a classic action policy (ECA) - would have 3 states (E, C, and A) with some logic (1 - task) in each state. - - - Simple selective: a policy with 1 state but more than - one task. Here, the appropriate task (and it’s logic) - will be selected at execution time. This policy is - very good for dealing with similar (or the same) - situation in different contexts. For instance, the - tasks can be related to available external software, - or to current work load on the compute node, or to - time of day. - - - Selective: any number of states having any number of - tasks (usually more than 1 task). This is a - combination of the two policies above, for instance an - ECA policy with more than one task in E, C, and A. - - - Classic directed: a policy with more than one state, - each having one task, but a non-sequential execution. - This means that the sequence of the states is not - pre-defined in the policy (as would be for all cases - above) but calculated at runtime. This can be good to - realize decision trees based on contextual - information. - - - Super Adaptive: using the full potential of the APEX - policy model, states and tasks and state execution are - fully flexible and calculated at runtime (per policy - execution). This policy is very close to a general - programming system (with only a few limitations), but - can solve very hard problems. - - .. container:: paragraph - - The final step is to select a response that the policy - creates. Possible responses have been discussed in the - literature for a very long time. A few examples are: - - .. container:: ulist - - - Obligation (deontic for what should happen) - - - Authorization (e.g. for rule-based or other access - control or security systems) + - Report, i.e. something did happen. An example is an event about an error or fault and the policy needs to + repair that problem. The policy would usually be an obligation, utility function, or goal policy. - - Intent (instead of providing detailed actions the - response is an intent statement and a further system - processes that) + - Monitoring, i.e. something does happen. An example is a notification about certain network conditions, to + which the policy might (or might not) react. The policy will mitigate the monitored events or permit (deny) + related actions as an obligation or authorization. - - Delegation (hand the problem over to someone else, - possibly with some information or instructions) + - Analysis, i.e. why did something happen. An example is an analytic component sends insights of a situation + requiring a policy to act on it. The policy can solve the problem, escalate it, or delegate it as a refrain or + delegation policy. - - Fail / Error (the policy has encountered a problem, - and reports it) + - Prediction, i.e. what will happen next. An example are events that a policy uses to predict a future network + condition. The policy can prevent or enforce the prediction as an adaptive policy, a utility function, or a goal. - - Feedback (why did the policy make a certain decision) + - Feedback, i.e. why did something happen or not happen. Similar to analysis, but here the feedback will be in + the input event and the policy needs to something with that information. Feedback can be related to history or + experience, for instance a previous policy execution. The policy needs to be context-aware or be a meta-policy. -APEX Policy Model -^^^^^^^^^^^^^^^^^ +.. container:: paragraph + + Once the purpose of the policy is decided, the next step is to look into what context information the policy will + require to do its job. This can range from very simple to a lot of different information, for instance: + +.. container:: ulist + + - No context, nothing but a trigger event, e.g. a string or a number, is required + + - Event context, the incoming event provides all information (more than a string or number) for the policy + + - Policy context (read only), the policy has access to additional information related to its class but cannot + change/alter them + + - Policy context (read and write), the policy has access to additional information related to its class and can + alter this information (for instance to record historic information) + + - Global context (read only), the policy has access to additional information of any kind but cannot + change/alter them + + - Global context (read and write), the policy the policy has access to additional information of any kind and + can alter this information (for instance to record historic information) + +.. container:: paragraph + + The next step is to decide how the policy should do its job, i.e. what flavor it has, how many states are needed, + and how many tasks. There are many possible combinations, for instance: + +.. container:: ulist + + - Simple / God: a simple policy with 1 state and 1 task, which is doing everything for the decision-making. This + is the ideal policy for simple situation, e.g. deciding on configuration parameters or simple access control. + + - Simple sequence: a simple policy with a number of states each having a single task. This is a very good policy + for simple decision-making with different steps. For instance, a classic action policy (ECA) would have 3 states + (E, C, and A) with some logic (1 task) in each state. + + - Simple selective: a policy with 1 state but more than one task. Here, the appropriate task (and it’s logic) + will be selected at execution time. This policy is very good for dealing with similar (or the same) situation in + different contexts. For instance, the tasks can be related to available external software, or to current work load + on the compute node, or to time of day. + + - Selective: any number of states having any number of tasks (usually more than 1 task). This is a combination + of the two policies above, for instance an ECA policy with more than one task in E, C, and A. + + - Classic directed: a policy with more than one state, each having one task, but a non-sequential execution. + This means that the sequence of the states is not pre-defined in the policy (as would be for all cases above) but + calculated at runtime. This can be good to realize decision trees based on contextual information. + + - Super Adaptive: using the full potential of the APEX policy model, states and tasks and state execution are + fully flexible and calculated at runtime (per policy execution). This policy is very close to a general + programming system (with only a few limitations), but can solve very hard problems. + +.. container:: paragraph -Introduction ------------- + The final step is to select a response that the policy creates. Possible responses have been discussed in the + literature for a very long time. A few examples are: - .. container:: paragraph +.. container:: ulist - The APEX policy model is shown in UML notation in the - figure below. A policy model can be stored in JSON or XML - format in a file or can be held in a database. The APEX - editor creates and modifies APEX policy models. APEX - deployment deploys policy models, and a policy model is - loaded into APEX engines so that the engines can run the - policies in the policy model. + - Obligation (deontic for what should happen) - .. container:: paragraph + - Authorization (e.g. for rule-based or other access control or security systems) - The figure shows four different views of the policy - model: + - Intent (instead of providing detailed actions the response is an intent statement and a further system + processes that) - .. container:: ulist + - Delegation (hand the problem over to someone else, possibly with some information or instructions) - - The general model view shows the main parts of a - policy: state, state output, event, and task. A task - can also have parameters. Data types can be defined on - a per-model basis using either standard atomic types - (such as character, string, numbers) or complex types - from a policy domain. + - Fail / Error (the policy has encountered a problem, and reports it) - - The logic model view emphasizes how decision-making - logic is injected into a policy. There are essentially - three different types of logic: task logic (for - decision making in a task), task selection logic (to - select a task if more than one is defined in a state), - and state finalizer logic (to compute the final output - event of a state and select an appropriate next state - from the policy model). + - Feedback (why did the policy make a certain decision) - - The context model view shows how context is injected - into a policy. States collect all context from their - tasks. A task can define what context it requires for - the decision making, i.e. what context the task logic - will process. Context itself is a collection of items - (individual context information) with data types. - Context can be templated. +***************** +APEX Policy Model +***************** + +.. container:: paragraph + + The APEX policy model is shown in UML notation in the figure below. A policy model can be stored in JSON or XML + format in a file or can be held in a database. The APEX editor creates and modifies APEX policy models. APEX + deployment deploys policy models, and a policy model is loaded into APEX engines so that the engines can run the + policies in the policy model. + +.. container:: paragraph + + The figure shows four different views of the policy model: + +.. container:: ulist - - The event and field model view shows the events in the - policy model. Tasks define what information they - consume (input) and produce (output). This information - is modeled as fields, essentially a key/type tuple in - the model and a key/type/value triple at execution. - Events then are collection of fields. + - The general model view shows the main parts of a policy: state, state output, event, and task. A task can also + have parameters. Data types can be defined on a per-model basis using either standard atomic types (such as + character, string, numbers) or complex types from a policy domain. - .. container:: imageblock + - The logic model view emphasizes how decision-making logic is injected into a policy. There are essentially + three different types of logic: task logic (for decision making in a task), task selection logic (to select a task + if more than one is defined in a state), and state finalizer logic (to compute the final output event of a state + and select an appropriate next state from the policy model). - .. container:: content + - The context model view shows how context is injected into a policy. States collect all context from their + tasks. A task can define what context it requires for the decision making, i.e. what context the task logic will + process. Context itself is a collection of items (individual context information) with data types. Context can be + templated. - |APEX Policy Model for Execution| + - The event and field model view shows the events in the policy model. Tasks define what information they + consume (input) and produce (output). This information is modeled as fields, essentially a key/type tuple in the + model and a key/type/value triple at execution. Events then are collection of fields. - .. container:: title +.. container:: imageblock - Figure 2. APEX Policy Model for Execution + .. container:: content + + |APEX Policy Model for Execution| + + .. container:: title + + Figure 2. APEX Policy Model for Execution Concepts and Keys -################# +================= - .. container:: paragraph +.. container:: paragraph - Each element of the policy model is called a - *concept*. Each *concept* is a subclass of the - abstract *Concept* class, as shown in the next figure. - Every concept implements the following abstract - methods: + Each element of the policy model is called a *concept*. Each *concept* is a subclass of the abstract *Concept* + class, as shown in the next figure. Every concept implements the following abstract methods: - .. container:: imageblock +.. container:: imageblock - .. container:: content + .. container:: content - |Concepts and Keys| + |Concepts and Keys| - .. container:: title + .. container:: title - Figure 3. Concepts and Keys + Figure 3. Concepts and Keys - .. container:: ulist +.. container:: ulist - - ``getKey()`` - gets the unique key for this concept - instance in the system + - ``getKey()`` - gets the unique key for this concept instance in the system - - ``validate()`` - validates the structure of this - concept, its sub-concepts and its relationships + - ``validate()`` - validates the structure of this concept, its sub-concepts and its relationships - - ``clean()`` - carries out housekeeping on the - concept such as trimming strings, remove any - hanging references + - ``clean()`` - carries out housekeeping on the concept such as trimming strings, remove any hanging references - - ``clone()`` - creates a deep copy of an instance of - this concept + - ``clone()`` - creates a deep copy of an instance of this concept - - ``equals()`` - checks if two instances of this - concept are equal + - ``equals()`` - checks if two instances of this concept are equal - - ``toString()`` - returns a string representation of - the concept + - ``toString()`` - returns a string representation of the concept - - ``hashCode()`` - returns a hash code for the - concept + - ``hashCode()`` - returns a hash code for the concept - - ``copyTo()`` - carries out a deep copy of one - instance of the concept to another instance, - overwriting the target fields. + - ``copyTo()`` - carries out a deep copy of one instance of the concept to another instance, overwriting the + target fields. - .. container:: paragraph +.. container:: paragraph - All concepts must have a *key*, which uniquely - identifies a concept instance. The *key* of a subclass - of an *Concept* must either be an ``ArtifactKey`` or - an ``ReferenceKey``. Concepts that have a stand-alone - independent existence such as *Policy*, *Task*, and - *Event* must have an ``ArtifctKey`` key. Concepts that - are contained in other concepts, that do not exist as - stand-alone concepts must have an ``ReferenceKey`` - key. Examples of such concepts are *State* and - *EventParameter*. + All concepts must have a *key*, which uniquely identifies a concept instance. The *key* of a subclass of an *Concept* + must either be an ``ArtifactKey`` or an ``ReferenceKey``. Concepts that have a stand-alone independent existence such + as *Policy*, *Task*, and *Event* must have an ``ArtifctKey`` key. Concepts that are contained in other concepts, that + do not exist as stand-alone concepts must have an ``ReferenceKey`` key. Examples of such concepts are *State* and + *EventParameter*. - .. container:: paragraph +.. container:: paragraph - An ``ArticactKey`` has two fields; the *Name* of the - concept it is the key for and the concept’s *Version*. - A concept’s name must be unique in a given - PolicyModel. A concept version is represented using - the well known *major.minor.path* scheme as used in - semantic versioning. + An ``ArticactKey`` has two fields; the *Name* of the concept it is the key for and the concept’s *Version*. A + concept’s name must be unique in a given PolicyModel. A concept version is represented using the well known + *major.minor.path* scheme as used in semantic versioning. - .. container:: paragraph +.. container:: paragraph - A ``ReferenceKey`` has three fields. The *UserKeyName* - and *UserKeyVersion* fields identify the - ``ArtifactKey`` of the concept in which the concept - keyed by the ``ReferenceKey`` is contained. The - *LocalName* field identifies the contained concept - instance. The *LocalName* must be unique in the - concepts of a given type contained by a parent. + A ``ReferenceKey`` has three fields. The *UserKeyName* and *UserKeyVersion* fields identify the ``ArtifactKey`` of + the concept in which the concept keyed by the ``ReferenceKey`` is contained. The *LocalName* field identifies the + contained concept instance. The *LocalName* must be unique in the concepts of a given type contained by a parent. - .. container:: paragraph +.. container:: paragraph - For example, a policy called ``SalesPolicy`` with a - Version of ``1.12.4`` has a state called ``Decide``. - The ``Decide`` state is linked to the ``SalesPolicy`` - with a ``ReferenceKey`` with fields *UserKeyName* of - ``SalesPolicy``, *UserKeyVersion* of ``1.12.4``, and - *LocalName* of ``Decide``. There must not be another - state called ``Decide`` in the policy ``SalesPolicy``. - However, there may well be a state called ``Decide`` - in some other policy called ``PurchasingPolicy``. + For example, a policy called ``SalesPolicy`` with a Version of ``1.12.4`` has a state called ``Decide``. The + ``Decide`` state is linked to the ``SalesPolicy`` with a ``ReferenceKey`` with fields *UserKeyName* of + ``SalesPolicy``, *UserKeyVersion* of ``1.12.4``, and *LocalName* of ``Decide``. There must not be another state + called ``Decide`` in the policy ``SalesPolicy``. However, there may well be a state called ``Decide`` in some other + policy called ``PurchasingPolicy``. - .. container:: paragraph +.. container:: paragraph - Each concept in the model is also a JPA (`Java - Persistence - API `__) - 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 `__ or - `EclipseLink `__. + Each concept in the model is also a JPA + (`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 `__ or `EclipseLink `__. Concept: PolicyModel -#################### +==================== - .. container:: paragraph +.. container:: paragraph - The *PolicyModel* concept is a container that holds - the definition of a set of policies and their - associated events, context maps, and tasks. A - *PolicyModel* is implemented as four maps for - policies, events, context maps, and tasks. Each map is - indexed by the key of the policy, event, context map, - or task. Any non-empty policy model must have at least - one entry in its policy, event, and task map because - all policies must have at least one input and output - event and must execute at least one task. + The *PolicyModel* concept is a container that holds the definition of a set of policies and their associated events, + context maps, and tasks. A *PolicyModel* is implemented as four maps for policies, events, context maps, and tasks. + Each map is indexed by the key of the policy, event, context map, or task. Any non-empty policy model must have at + least one entry in its policy, event, and task map because all policies must have at least one input and output event + and must execute at least one task. - .. container:: paragraph +.. container:: paragraph - A *PolicyModel* concept is keyed with an - ``ArtifactKey key``. Because a *PolicyModel* is an - ``AxConcept``, calling the ``validate()`` method on a - policy model validates the concepts, structure, and - relationships of the entire policy model. + A *PolicyModel* concept is keyed with an ``ArtifactKey key``. Because a *PolicyModel* is an ``AxConcept``, calling + the ``validate()`` method on a policy model validates the concepts, structure, and relationships of the entire policy + model. Concept: DataType -################# +================= + +.. container:: paragraph - .. container:: paragraph - - Data types are tightly controlled in APEX in order to - provide a very high degree of consistency in policies - and to facilitate tracking of changes to context as - policies execute. All context is modeled as a - *DataType* concept. Each DataType concept instance is - keyed with an ``ArtifactKey`` key. The DataType field - identifies the Java class of objects that is used to - represent concept instances that use this data type. - All context has a *DataType*; incoming and outgoing - context is represented by *EventField* concepts and - all other context is represented by *ContextItem* - concepts. + Data types are tightly controlled in APEX in order to provide a very high degree of consistency in policies and to + facilitate tracking of changes to context as policies execute. All context is modeled as a *DataType* concept. Each + DataType concept instance is keyed with an ``ArtifactKey`` key. The DataType field identifies the Java class of + objects that is used to represent concept instances that use this data type. All context has a *DataType*; incoming + and outgoing context is represented by *EventField* concepts and all other context is represented by *ContextItem* + concepts. Concept: Event -############## +============== - .. container:: paragraph +.. container:: paragraph - An *Event* defines the structure of a message that - passes into or out of an APEX engine or that passes - between two states in an APEX engine. APEX supports - message reception and sending in many formats and all - messages are translated into an *Event* prior to - processing by an APEX engine. Event concepts are keyed - with an ``ArtifactKey`` key. The parameters of an - event are held as a map of *EventField* concept - instances with each parameter indexed by the - *LocalName* of its ``ReferenceKey``. An *Event* has - three fields: + An *Event* defines the structure of a message that passes into or out of an APEX engine or that passes between two + states in an APEX engine. APEX supports message reception and sending in many formats and all messages are translated + into an *Event* prior to processing by an APEX engine. Event concepts are keyed with an ``ArtifactKey`` key. The + parameters of an event are held as a map of *EventField* concept instances with each parameter indexed by the + *LocalName* of its ``ReferenceKey``. An *Event* has three fields: - .. container:: ulist +.. container:: ulist - - The *NameSpace* identifies the domain of - application of the event + - The *NameSpace* identifies the domain of application of the event - - The *Source* of the event identifies the system - that emitted the event + - The *Source* of the event identifies the system that emitted the event - - The *Target* of the event identifies the system - that the event was sent to + - The *Target* of the event identifies the system that the event was sent to - .. container:: paragraph +.. container:: paragraph - A *PolicyModel* contains a map of all the events known - to a given policy model. Although an empty model may - have no events in its event map, any sane policy model - must have at least one *Event* defined. + A *PolicyModel* contains a map of all the events known to a given policy model. Although an empty model may have no + events in its event map, any sane policy model must have at least one *Event* defined. Concept: EventField -################### +=================== - .. container:: paragraph +.. container:: paragraph - The incoming context and outgoing context of an event - are the fields of the event. Each field representing a - single piece of incoming or outgoing context. Each - field of an *Event* is represented by an instance of - the *EventField* concept. Each *EventField* concept - instance in an event is keyed with a ``ReferenceKey`` - key, which references the event. The *LocalName* field - of the ``ReferenceKey`` holds the name of the field A - reference to a *DataType* concept defines the data - type that values of this parameter have at run time. + The incoming context and outgoing context of an event are the fields of the event. Each field representing a single + piece of incoming or outgoing context. Each field of an *Event* is represented by an instance of the *EventField* + concept. Each *EventField* concept instance in an event is keyed with a ``ReferenceKey`` key, which references the + event. The *LocalName* field of the ``ReferenceKey`` holds the name of the field A reference to a *DataType* concept + defines the data type that values of this parameter have at run time. Concept: ContextMap -################### - - .. container:: paragraph - - The set of context that is available for use by the - policies of a *PolicyModel* is defined as *ContextMap* - concept instances. The *PolicyModel* holds a map of - all the *ContextMap* definitions. A *ContextMap* is - itself a container for a group of related context - items, each of which is represented by a *ContextItem* - concept instance. *ContextMap* concepts are keyed with - an ``ArtifactKey`` key. A developer can use the APEX - Policy Editor to create context maps for their - application domain. - - .. container:: paragraph - - A *ContextMap* uses a map to hold the context items. - The ContextItem concept instances in the map are - indexed by the *LocalName* of their ``ReferenceKey``. - - .. container:: paragraph - - The *ContextMapType* field of a *ContextMap* defines - the type of a context map. The type can have either of - two values: - - .. container:: ulist - - - A *BAG* context map is a context map with fixed - content. Each possible context item in the context - map is defined at design time and is held in the - *ContextMap* context instance as *ContextItem* - concept definitions and only the values of the - context items in the context map can be changed at - run time. The context items in a *BAG* context map - have mixed types and distinct *ContextItem* concept - instances of the same type can be defined. A *BAG* - context map is convenient for defining a group of - context items that are diverse but are related by - domain, such as the characteristics of a device. A - fully defined *BAG* context map has a fully - populated *ContextItem* map but its - *ContextItemTemplate* reference is not defined. - - - A *SAMETYPE* context map is used to represent a - group of *ContextItem* instances of the same type. - Unlike a *BAG* context map, the *ContextItem* - concept instances of a *SAMETYPE* context map can - be added, modified, and deleted at runtime. All - *ContextItem* concept instances in a *SAMETYPE* - context map must be of the same type, and that - context item is defined as a single - *ContextItemTemplate* concept instances at design - time. At run time, the *ContextItemTemplate* - definition is used to create new *ContextItem* - concept instances for the context map on demand. A - fully defined *SAMETYPE context map has an empty - ContextItem map and its ContextItemTemplate\_* - reference is defined. - - .. container:: paragraph - - The *Scope* of a *ContextMap* defines the range of - applicability of a context map in APEX. The following - scopes of applicability are defined: - - .. container:: ulist - - - *EPHEMERAL* scope means that the context map is - owned, used, and modified by a single application, - but the context map only exists while that - application is running - - - *APPLICATION* scope specifies that the context map - is owned, used, and modified by a single - application, the context map is persistent - - - *GLOBAL* scope specifies that the context map is - globally owned and is used and modified by any - application, the context map is persistent - - - *EXTERNAL* scope specifies that the context map is - owned by an external system and may be used in a - read-only manner by any application, the context - map is persistent - - .. container:: paragraph - - A much more sophisticated scoping mechanism for - context maps is envisaged for Apex in future work. In - such a mechanism, the scope of a context map would - work somewhat like the way roles work in security - authentication systems. +=================== + +.. container:: paragraph + + The set of context that is available for use by the policies of a *PolicyModel* is defined as *ContextMap* concept + instances. The *PolicyModel* holds a map of all the *ContextMap* definitions. A *ContextMap* is itself a container + for a group of related context items, each of which is represented by a *ContextItem* concept instance. *ContextMap* + concepts are keyed with an ``ArtifactKey`` key. A developer can use the APEX Policy Editor to create context maps for + their application domain. + +.. container:: paragraph + + A *ContextMap* uses a map to hold the context items. The ContextItem concept instances in the map are indexed by the + *LocalName* of their ``ReferenceKey``. + +.. container:: paragraph + + The *ContextMapType* field of a *ContextMap* defines the type of a context map. The type can have either of two + values: + +.. container:: ulist + + - A *BAG* context map is a context map with fixed content. Each possible context item in the context map is + defined at design time and is held in the *ContextMap* context instance as *ContextItem* concept definitions and + only the values of the context items in the context map can be changed at run time. The context items in a *BAG* + context map have mixed types and distinct *ContextItem* concept instances of the same type can be defined. A *BAG* + context map is convenient for defining a group of context items that are diverse but are related by domain, such as + the characteristics of a device. A fully defined *BAG* context map has a fully populated *ContextItem* map but its + *ContextItemTemplate* reference is not defined. + + - A *SAMETYPE* context map is used to represent a group of *ContextItem* instances of the same type. Unlike a + *BAG* context map, the *ContextItem* concept instances of a *SAMETYPE* context map can be added, modified, and + deleted at runtime. All *ContextItem* concept instances in a *SAMETYPE* context map must be of the same type, and + that context item is defined as a single *ContextItemTemplate* concept instances at design time. At run time, the + *ContextItemTemplate* definition is used to create new *ContextItem* concept instances for the context map on + demand. A fully defined *SAMETYPE context map has an empty ContextItem map and its ContextItemTemplate\_* + reference is defined. + +.. container:: paragraph + + The *Scope* of a *ContextMap* defines the range of applicability of a context map in APEX. The following scopes of + applicability are defined: + +.. container:: ulist + + - *EPHEMERAL* scope means that the context map is owned, used, and modified by a single application but the + context map only exists while that application is running + + - *APPLICATION* scope specifies that the context map is owned, used, and modified by a single application, the + context map is persistent + + - *GLOBAL* scope specifies that the context map is globally owned and is used and modified by any application, + the context map is persistent + + - *EXTERNAL* scope specifies that the context map is owned by an external system and may be used in a read-only + manner by any application, the context map is persistent + +.. container:: paragraph + + A much more sophisticated scoping mechanism for context maps is envisaged for Apex in future work. In such a + mechanism, the scope of a context map would work somewhat like the way roles work in security authentication systems. Concept: ContextItem -#################### - - .. container:: paragraph - - Each piece of context in a *ContextMap* is represented - by an instance of the *ContextItem* concept. Each - *ContextItem* concept instance in a context map keyed - with a ``ReferenceKey`` key, which references the - context map of the context item. The *LocalName* field - of the ``ReferenceKey`` holds the name of the context - item in the context map A reference to a *DataType* - concept defines the data type that values of this - context item have at run time. The *WritableFlag* - indicates if the context item is read only or - read-write at run time. +==================== + +.. container:: paragraph + + Each piece of context in a *ContextMap* is represented by an instance of the *ContextItem* concept. Each + *ContextItem* concept instance in a context map keyed with a ``ReferenceKey`` key, which references the context map + of the context item. The *LocalName* field of the ``ReferenceKey`` holds the name of the context item in the context + map A reference to a *DataType* concept defines the data type that values of this context item have at run time. The + *WritableFlag* indicates if the context item is read only or read-write at run time. Concept: ContextItemTemplate -############################ - - .. container:: paragraph - - In a *SAMETYPE* *ContextMap*, the - *ContextItemTemplate* definition provides a template - for the *ContextItem* instances that will be created - on the context map at run time. Each *ContextItem* - concept instance in the context map is created using - the *ContextItemTemplate* template. It is keyed with a - ``ReferenceKey`` key, which references the context map - of the context item. The *LocalName* field of the - ``ReferenceKey``, supplied by the creator of the - context item at run time, holds the name of the - context item in the context map. A reference to a - *DataType* concept defines the data type that values - of this context item have at run time. The - *WritableFlag* indicates if the context item is read - only or read-write at run time. +============================ + +.. container:: paragraph + + In a *SAMETYPE* *ContextMap*, the *ContextItemTemplate* definition provides a template for the *ContextItem* + instances that will be created on the context map at run time. Each *ContextItem* concept instance in the context map + is created using the *ContextItemTemplate* template. It is keyed with a ``ReferenceKey`` key, which references the + context map of the context item. The *LocalName* field of the ``ReferenceKey``, supplied by the creator of the + context item at run time, holds the name of the context item in the context map. A reference to a *DataType* concept + defines the data type that values of this context item have at run time. The *WritableFlag* indicates if the context + item is read only or read-write at run time. Concept: Task -############# - - .. container:: paragraph - - The smallest unit of logic in a policy is a *Task*. A - task encapsulates a single atomic unit of logic, and - is designed to be a single indivisible unit of - execution. A task may be invoked by a single policy or - by many policies. A task has a single trigger event, - which is sent to the task when it is invoked. Tasks - emit one or more outgoing events, which carry the - result of the task execution. Tasks may use or modify - context as they execute. - - .. container:: paragraph - - The Task concept definition captures the definition of - an APEX task. Task concepts are keyed with an - ``ArtifactKey`` key. The Trigger of the task is a - reference to the *Event* concept that triggers the - task. The *OutgoingEvents* of a task are a set of - references to *Event* concepts that may be emitted by - the task. - - .. container:: paragraph - - All tasks have logic, some code that is programmed to - execute the work of the task. The *Logic* concept of - the task holds the definition of that logic. - - .. container:: paragraph - - The *Task* definition holds a set of *ContextItem* and - *ContextItemTemplate* context items that the task is - allow to access, as defined by the task developer at - design time. The type of access (read-only or read - write) that a task has is determined by the - *WritableFlag* flag on the individual context item - definitions. At run time, a task may only access the - context items specified in its context item set, the - APEX engine makes only the context items in the task - context item set is available to the task. - - .. container:: paragraph - - A task can be configured with startup parameters. The - set of parameters that can be configured on a task are - defined as a set of *TaskParameter* concept - definitions. +============= + +.. container:: paragraph + + The smallest unit of logic in a policy is a *Task*. A task encapsulates a single atomic unit of logic, and is + designed to be a single indivisible unit of execution. A task may be invoked by a single policy or by many policies. + A task has a single trigger event, which is sent to the task when it is invoked. Tasks emit one or more outgoing + events, which carry the result of the task execution. Tasks may use or modify context as they execute. + +.. container:: paragraph + + The Task concept definition captures the definition of an APEX task. Task concepts are keyed with an ``ArtifactKey`` + key. The Trigger of the task is a reference to the *Event* concept that triggers the task. The *OutgoingEvents* of a + task are a set of references to *Event* concepts that may be emitted by the task. + +.. container:: paragraph + + All tasks have logic, some code that is programmed to execute the work of the task. The *Logic* concept of the task + holds the definition of that logic. + +.. container:: paragraph + + The *Task* definition holds a set of *ContextItem* and *ContextItemTemplate* context items that the task is allow to + access, as defined by the task developer at design time. The type of access (read-only or read write) that a task has + is determined by the *WritableFlag* flag on the individual context item definitions. At run time, a task may only + access the context items specified in its context item set, the APEX engine makes only the context items in the task + context item set is available to the task. + +.. container:: paragraph + + A task can be configured with startup parameters. The set of parameters that can be configured on a task are defined + as a set of *TaskParameter* concept definitions. Concept: TaskParameter -###################### +====================== - .. container:: paragraph +.. container:: paragraph - Each configuration parameter of a task are represented - as a *Taskparameter* concept keyed with a - ``ReferenceKey`` key, which references the task. The - *LocalName* field of the ``ReferenceKey`` holds the - name of the parameter. The *DefaultValue* field - defines the default value that the task parameter is - set to. The value of *TaskParameter* instances can be - overridden at deployment time by specifying their - values in the configuration information passed to APEX - engines. + Each configuration parameter of a task are represented as a *Taskparameter* concept keyed with a ``ReferenceKey`` + key, which references the task. The *LocalName* field of the ``ReferenceKey`` holds the name of the parameter. The + *DefaultValue* field defines the default value that the task parameter is set to. The value of *TaskParameter* + instances can be overridden at deployment time by specifying their values in the configuration information passed to + APEX engines. - .. container:: paragraph +.. container:: paragraph - The *taskParameters* field is specified under *engineParameters* - in the ApexConfig. It can contain one or more task parameters, where each - item can contain the parameter key, value as well as the taskId to which it is associated. - If the taskId is not specified, then the parameters are added to all tasks. + The *taskParameters* field is specified under *engineParameters* in the ApexConfig. It can contain one or more task + parameters, where each item can contain the parameter key, value as well as the taskId to which it is associated. If + the taskId is not specified, then the parameters are added to all tasks. Concept: Logic -############## - - .. container:: paragraph - - The *Logic* concept instance holds the actual - programmed task logic for a task defined in a *Task* - concept or the programmed task selection logic for a - state defined in a *State* concept. It is keyed with a - ``ReferenceKey`` key, which references the task or - state that owns the logic. The *LocalName* field of - the Logic concept is the name of the logic. - - .. container:: paragraph - - The *LogicCode* field of a Logic concept definition is - a string that holds the program code that is to be - executed at run time. The *LogicType* field defines - the language of the code. The standard values are the - logic languages supported by APEX: - `JAVASCRIPT `__, - `JAVA `__, - `JYTHON `__, - `JRUBY `__, or - `MVEL `__. - - .. container:: paragraph - - The APEX engine uses the *LogicType* field value to - decide which language interpreter to use for a task - and then sends the logic defined in the *LogicCode* - field to that interpreter. +============== + +.. container:: paragraph + + The *Logic* concept instance holds the actual programmed task logic for a task defined in a *Task* concept or the + programmed task selection logic for a state defined in a *State* concept. It is keyed with a ``ReferenceKey`` key, + which references the task or state that owns the logic. The *LocalName* field of the Logic concept is the name of the + logic. + +.. container:: paragraph + + The *LogicCode* field of a Logic concept definition is a string that holds the program code that is to be executed + at run time. The *LogicType* field defines the language of the code. The standard values are the logic languages + supported by APEX: `JAVASCRIPT `__, `JAVA `__, + `JYTHON `__, `JRUBY `__, or + `MVEL `__. + +.. 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 `__ - and require a sequence of four states: namely - *Match*, *Establish*, *Decide* and *Act*. - - - The *OODA* flavor supports policies written to the - `OODA loop - pattern `__ - 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 `__ - and require a sequence of three states: namely - *Event*, *Condition* and *Action* - - - The *XACML* flavor supports policies written in - `XACML `__ and - require a single state: namely *XACML* - - - The *FREEFORM* flavor supports policies written in - an arbitrary style. A user can define a *FREEFORM* - policy as an arbitrarily long chain of states. - - .. container:: paragraph - - The *FirstState* field of a *Policy* definition is the - starting point for execution of a policy. Therefore, - the trigger event of the state referenced in the - *FirstState* field is also the trigger event for the - entire policy. +=============== + +.. container:: paragraph + + The *Policy* concept defines a policy in APEX. The definition is rather straightforward. A policy is made up of a + set of states with the flavor of the policy determining the structure of the policy states and the first state + defining what state in the policy executes first. *Policy* concepts are keyed with an ``ArtifactKey`` key. + +.. container:: paragraph + + The *PolicyFlavour* of a *Policy* concept specifies the structure that will be used for the states in the policy. A + number of commonly used policy patterns are supported as APEX policy flavors. The standard policy flavors are: + +.. container:: ulist + + - The *MEDA* flavor supports policies written to the + `MEDA policy pattern `__ + and require a sequence of four states: namely *Match*, *Establish*, *Decide* and *Act*. + + - The *OODA* flavor supports policies written to the + `OODA loop pattern `__ 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 `__ and require a sequence of three + states: namely *Event*, *Condition* and *Action* + + - The *XACML* flavor supports policies written in `XACML `__ and require a + single state: namely *XACML* + + - The *FREEFORM* flavor supports policies written in an arbitrary style. A user can define a *FREEFORM* policy + as an arbitrarily long chain of states. + +.. container:: paragraph + + The *FirstState* field of a *Policy* definition is the starting point for execution of a policy. Therefore, the + trigger event of the state referenced in the *FirstState* field is also the trigger event for the entire policy. Concept: State -############## - - .. container:: paragraph - - The *State* concept represents a phase or a stage in a - policy, with a policy being composed of a series of - states. Each state has at least one but may have many - tasks and, on each run of execution, a state executes - one and only one of its tasks. If a state has more - than one task, then its task selection logic is used - to select which task to execute. Task selection logic - is programmable logic provided by the state designer. - That logic can use incoming, policy, global, and - external context to select which task best - accomplishes the purpose of the state in a give - situation if more than one task has been specified on - a state. A state calls one and only one task when it - is executed. - - .. container:: paragraph - - Each state is triggered by an event, which means that - all tasks of a state must also be triggered by that - same event. The set of output events for a state is - the union of all output events from all tasks for that - task. In practice at the moment, because a state can - only have a single input event, a state that is not - the final state of a policy may only output a single - event and all tasks of that state may also only output - that single event. In future work, the concept of - having a less restrictive trigger pattern will be - examined. - - .. container:: paragraph - - A *State* concept is keyed with a ``ReferenceKey`` - key, which references the *Policy* concept that owns - the state. The *LocalName* field of the - ``ReferenceKey`` holds the name of the state. As a - state is part of a chain of states, the *NextState* - field of a state holds the ``ReferenceKey`` key of the - state in the policy to execute after this state. - - .. container:: paragraph - - The *Trigger* field of a state holds the - ``ArtifactKey`` of the event that triggers this state. - The *OutgoingEvents* field holds the ``ArtifactKey`` - references of all possible events that may be output - from the state. This is a set that is the union of all - output events of all tasks of the state. - - .. container:: paragraph - - The *Task* concepts that hold the definitions of the - task for the state are held as a set of - ``ArtifactKey`` references in the state. The - *DefaultTask* field holds a reference to the default - task for the state, a task that is executed if no task - selection logic is specified. If the state has only - one task, that task is the default task. - - .. container:: paragraph - - The *Logic* concept referenced by a state holds the - task selection logic for a state. The task selection - logic uses the incoming context (parameters of the - incoming event) and other context to determine the - best task to use to execute its goals. The state holds - a set of references to *ContextItem* and - *ContextItemTemplate* definitions for the context used - by its task selection logic. +============== + +.. container:: paragraph + + The *State* concept represents a phase or a stage in a policy, with a policy being composed of a series of states. + Each state has at least one but may have many tasks and, on each run of execution, a state executes one and only one + of its tasks. If a state has more than one task, then its task selection logic is used to select which task to + execute. Task selection logic is programmable logic provided by the state designer. That logic can use incoming, + policy, global, and external context to select which task best accomplishes the purpose of the state in a give + situation if more than one task has been specified on a state. A state calls one and only one task when it is + executed. + +.. container:: paragraph + + Each state is triggered by an event, which means that all tasks of a state must also be triggered by that same + event. The set of output events for a state is the union of all output events from all tasks for that task. In + practice at the moment, because a state can only have a single input event, a state that is not the final state of a + policy may only output a single event and all tasks of that state may also only output that single event. In future + work, the concept of having a less restrictive trigger pattern will be examined. + +.. container:: paragraph + + A *State* concept is keyed with a ``ReferenceKey`` key, which references the *Policy* concept that owns the state. + The *LocalName* field of the ``ReferenceKey`` holds the name of the state. As a state is part of a chain of states, + the *NextState* field of a state holds the ``ReferenceKey`` key of the state in the policy to execute after this + state. + +.. container:: paragraph + + The *Trigger* field of a state holds the ``ArtifactKey`` of the event that triggers this state. The *OutgoingEvents* + field holds the ``ArtifactKey`` references of all possible events that may be output from the state. This is a set + that is the union of all output events of all tasks of the state. + +.. container:: paragraph + The *Task* concepts that hold the definitions of the task for the state are held as a set of ``ArtifactKey`` + references in the state. The *DefaultTask* field holds a reference to the default task for the state, a task that is + executed if no task selection logic is specified. If the state has only one task, that task is the default task. + +.. container:: paragraph + + The *Logic* concept referenced by a state holds the task selection logic for a state. The task selection logic uses + the incoming context (parameters of the incoming event) and other context to determine the best task to use to + execute its goals. The state holds a set of references to *ContextItem* and *ContextItemTemplate* definitions for the + context used by its task selection logic. + +************* Writing Logic -^^^^^^^^^^^^^ +************* Writing APEX Task Logic ------------------------ - - .. container:: paragraph - - Task logic specifies the behavior of an Apex Task. This - logic can be specified in a number of ways, exploiting - Apex’s plug-in architecture to support a range of logic - executors. In Apex scripted Task Logic can be written in - any of these languages: - - .. container:: ulist - - - ```MVEL`` `__, - - - ```JavaScript`` `__, - - - ```JRuby`` `__ or - - - ```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 | .. 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 | .. 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 `` | -| | interface to get and set context as appropriate. The returned | -| | ``ContextAlbum`` also has methods to lock context albums, get | -| | information about the schema of the items to be stored in a context | -| | album, and get a ``SchemaHelper`` to manipulate context album items. How | -| | to define and use context in a task is described in the Apex | -| | Programmer’s Guide and in the My First Apex Policy guide. | -| | | -| | .. container:: | -| | | -| | .. container:: content | -| | | -| | .. container:: paragraph | -| | | -| | **Example:** | -| | | -| | .. code:: javascript | -| | | -| | var bkey = executor.inFields.get("branch_ID"); | -| | var cnts = executor.getContextMap("BranchCounts"); | -| | cnts.lockForWriting(bkey); | -| | cnts.put(bkey, cnts.get(bkey) + 1); | -| | cnts.unlockForWriting(bkey); | -+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+ +======================= -Writing APEX Task Selection Logic ---------------------------------- +.. container:: paragraph + + Task logic specifies the behavior of an Apex Task. This logic can be specified in a number of ways, exploiting + Apex’s plug-in architecture to support a range of logic executors. In Apex scripted Task Logic can be written in any + of these languages: + +.. container:: ulist + + - ```MVEL`` `__, + + - ```JavaScript`` `__, + + - ```JRuby`` `__ or + + - ```Jython`` `__. - .. container:: paragraph - - The function of Task Selection Logic is to choose which task - should be executed for an Apex State as one of the steps in an - Apex Policy. Since each state must define a default task there is - no need for Task Selection Logic unless the state uses more than - one task. This logic can be specified in a number of ways, - exploiting Apex’s plug-in architecture to support a range of logic - executors. In Apex scripted Task Selection Logic can be written in - any of these languages: - - .. container:: ulist - - - ```MVEL`` `__, - - - ```JavaScript`` `__, - - - ```JRuby`` `__ or - - - ```Jython`` `__. - - .. container:: paragraph - - These languages were chosen because the scripts can be compiled - into Java bytecode at runtime and then efficiently executed - natively in the JVM. Task Selection Logic an also be written - directly in Java but needs to be compiled, with the resulting - classes added to the classpath. There are also a number of other - Task Selection Logic types but these are not supported as yet. - This guide will focus on the scripted Task Selection Logic - approaches, with MVEL and JavaScript being our favorite languages. - In particular this guide will focus on the Apex aspects of the - scripts. However, this guide does not attempt to teach you about - the scripting languages themselves …​ that is up to you! - - .. tip:: - JVM-based scripting languages - For more more information on Scripting for the Java platform see: - https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/index.html - - .. note:: - What does Task Selection Logic do? - When an Apex state references multiple tasks, there must be a way to dynamically decide - which task should be chosen and executed. This can depend on the many factors, e.g. the - *incoming event for the state*, *shared state* or *context*, *external context*, - etc.. This is the function of a state’s Task Selection Logic. Obviously, if there is - only one task then Task only one task then Task Selection Logic is not needed. - Each state must also select one of the tasks a the *default state*. If the Task - Selection Logic is unable to select an appropriate task, then it should select the - *default task*. Once the task has been selected the Apex Engine will then execute that - task. - - .. container:: paragraph - - First lets start with some simple Task Selection Logic, drawn from - the "My First Apex Policy" example: The Task Selection Logic from - the "My First Apex Policy" example is specified in JavaScript - here: - - .. container:: listingblock - - .. container:: title - - Javascript code for the "My First Policy" Task Selection Logic - - .. container:: content - - .. code:: javascript - - /* - * ============LICENSE_START======================================================= - * Copyright (C) 2016-2018 Ericsson. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - - - var returnValueType = Java.type("java.lang.Boolean"); - var returnValue = new returnValueType(true); - - executor.logger.info("Task Selection Execution: '"+executor.subject.id+ - "'. Input Event: '"+executor.inFields+"'"); - - branchid = executor.inFields.get("branch_ID"); - taskorig = executor.subject.getTaskKey("MorningBoozeCheck"); - taskalt = executor.subject.getTaskKey("MorningBoozeCheckAlt1"); - taskdef = executor.subject.getDefaultTaskKey(); - - if(branchid >=0 && branchid <1000){ - taskorig.copyTo(executor.selectedTask); - } - else if (branchid >=1000 && branchid <2000){ - taskalt.copyTo(executor.selectedTask); - } - else{ - taskdef.copyTo(executor.selectedTask); - } - - /* - This task selection logic selects task "MorningBoozeCheck" for branches with - 0<=branch_ID<1000 and selects task "MorningBoozeCheckAlt1" for branches with - 1000<=branch_ID<2000. Otherwise the default task is selected. - In this case the default task is also "MorningBoozeCheck" - */ - - .. container:: paragraph - - The role of the Task Selection Logic in this simple example is to - examine the value in one incoming field (``branchid``), then - depending on that field’s value set the value for the selected - task to the appropriate task (``MorningBoozeCheck``, - ``MorningBoozeCheckAlt1``, or the default task). - - .. container:: paragraph - - Another thing to notice is that Task Selection Logic should return - a ``java.lang.Boolean`` value ``true`` if the logic executed - correctly. If the logic fails for some reason then ``false`` can - be returned, but this will cause the policy invoking this task - will fail and exit. - - .. note:: - How to return a value from Task Selection Logic - Some languages explicitly support returning values from the script (e.g. MVEL and - JRuby) using an explicit return statement (e.g. ``return true``), other languages do not (e.g. - JavaScript and Jython). For languages that do not support the ``return`` statement, a special field called - ``returnValue`` must be created to hold the result of the task logic operation (i.e. assign a ``java.lang.Boolean`` - value to the ``returnValue`` field before completing the task). - Also, in MVEL if there is not explicit return statement then the return value of the last executed statement will - return (e.g. the statement a=(1+2) will return the value 3). - - .. container:: paragraph - - Each of the scripting languages used in Apex can import and use - standard Java libraries to perform complex tasks. Besides imported - classes and normal language features Apex provides some natively - available parameters and functions that can be used directly. At - run-time these parameters are populated by the Apex execution - environment and made natively available to logic scripts each time - the logic script is invoked. (These can be accessed using the - ``executor`` keyword for most languages, or can be accessed - directly without the ``executor`` keyword in MVEL): - - Table 2. The ``executor`` Fields / Methods - +-------------------------------------------------------+--------------------------------------------------------+ - | Unix, Cygwin | Windows | - +=======================================================+========================================================+ - | .. container:: | .. container:: | - | | | - | .. container:: content | .. container:: content | - | | | - | .. code:: bash | .. code:: bash | - | :number-lines: | :number-lines: | - | | | - | >c: | # cd /usr/local/src/apex-pdp | - | >cd \dev\apex | # mvn clean install -DskipTests | - | >mvn clean install -DskipTests | | - +-------------------------------------------------------+--------------------------------------------------------+ - -+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+ -| Name | Type | Java type | Description | -+============+=============+================================+=====================================================================================+ -| inFields | Fields | java.util.Map | .. 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 | .. container:: paragraph | -| | | | | -| | | | The outgoing task fields. This is implemented as a standard initially empty Java | -| | | | (modifiable) Map. To create a new schema-compliant instance of a field object | -| | | | see the utility method subject.getOutFieldSchemaHelper() below | -| | | | | -| | | | .. container:: | -| | | | | -| | | | .. container:: content | -| | | | | -| | | | .. container:: paragraph | -| | | | | -| | | | **Example:** | -| | | | | -| | | | .. code:: javascript | -| | | | | -| | | | executor.outFields["authorised"] = false; | -+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+ -| logger | Logger | org.slf4j.ext.XLogger | .. container:: paragraph | -| | | | | -| | | | A helpful logger | -| | | | | -| | | | .. container:: | -| | | | | -| | | | .. container:: content | -| | | | | -| | | | .. container:: paragraph | -| | | | | -| | | | **Example:** | -| | | | | -| | | | .. code:: javascript | -| | | | | -| | | | executor.logger.info("Executing task: " | -| | | | +executor.subject.id); | -+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+ -| TRUE/FALSE | boolean | java.lang.Boolean | .. container:: paragraph | -| | | | | -| | | | 2 helpful constants. These are useful to retrieve correct return values for the | -| | | | task logic | -| | | | | -| | | | .. container:: | -| | | | | -| | | | .. container:: content | -| | | | | -| | | | .. container:: paragraph | -| | | | | -| | | | **Example:** | -| | | | | -| | | | .. code:: javascript | -| | | | | -| | | | var returnValue = executor.isTrue; | -| | | | var returnValueType = Java.type("java.lang.Boolean"); | -| | | | var returnValue = new returnValueType(true); | -+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+ -| subject | Task | TaskFacade | .. container:: paragraph | -| | | | | -| | | | This provides some useful information about the task that contains this task | -| | | | logic. This object has some useful fields and methods : | -| | | | | -| | | | .. container:: ulist | -| | | | | -| | | | - **AxTask task** to get access to the full task definition of | -| | | | the host task | -| | | | | -| | | | - **String getTaskName()** to get the name of the host task | -| | | | | -| | | | - **String getId()** to get the ID of the host task | -| | | | | -| | | | - **SchemaHelper getInFieldSchemaHelper( String fieldName )** to | -| | | | get a ``SchemaHelper`` helper object to manipulate incoming | -| | | | task fields in a schema-aware manner | -| | | | | -| | | | - **SchemaHelper getOutFieldSchemaHelper( String fieldName )** to | -| | | | get a ``SchemaHelper`` helper object to manipulate outgoing | -| | | | task fields in a schema-aware manner, e.g. to instantiate new | -| | | | schema-compliant field objects to populate the | -| | | | ``executor.outFields`` outgoing fields map | -| | | | | -| | | | .. container:: | -| | | | | -| | | | .. container:: content | -| | | | | -| | | | .. container:: paragraph | -| | | | | -| | | | **Example:** | -| | | | | -| | | | .. code:: javascript | -| | | | | -| | | | executor.logger.info("Task name: " | -| | | | +executor.subject.getTaskName()); | -| | | | executor.logger.info("Task id: " | -| | | | +executor.subject.getId()); | -| | | | executor.logger.info("Task inputs definitions: " | -| | | | +"executor.subject.task.getInputFieldSet()); | -| | | | executor.logger.info("Task outputs definitions: " | -| | | | +"executor.subject.task.getOutputFieldSet()); | -| | | | executor.outFields["authorised"] = executor.subject | -| | | | .getOutFieldSchemaHelper("authorised") | -| | | | .createNewInstance("false"); | -+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+ -| parameters | Fields | java.util.Map | .. container:: paragraph | -| | | | | -| | | | All parameters in the current task. This is implemented as a standard Java Map. | -| | | | | -| | | | .. container:: | -| | | | | -| | | | .. container:: content | -| | | | | -| | | | .. container:: paragraph | -| | | | | -| | | | **Example:** | -| | | | | -| | | | .. code:: javascript | -| | | | | -| | | | executor.parameters.get("ParameterKey1")) | -+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+ -| ContextAlbum getContextAlbum(String ctxtAlbumName ) | .. container:: paragraph | -| | | -| | A utility method to retrieve a ``ContextAlbum`` for use in the task. | -| | This is how you access the context used by the task. The returned | -| | ``ContextAlbum`` implements the ``java.util.Map `` | -| | interface to get and set context as appropriate. The returned | -| | ``ContextAlbum`` also has methods to lock context albums, get | -| | information about the schema of the items to be stored in a context | -| | album, and get a ``SchemaHelper`` to manipulate context album items. How | -| | to define and use context in a task is described in the Apex | -| | Programmer’s Guide and in the My First Apex Policy guide. | -| | | -| | .. container:: | -| | | -| | .. container:: content | -| | | -| | .. container:: paragraph | -| | | -| | **Example:** | -| | | -| | .. code:: javascript | -| | | -| | var bkey = executor.inFields.get("branch_ID"); | -| | var cnts = executor.getContextMap("BranchCounts"); | -| | cnts.lockForWriting(bkey); | -| | cnts.put(bkey, cnts.get(bkey) + 1); | -| | cnts.unlockForWriting(bkey); | -+------------+-------------+--------------------------------+-------------------------------------------------------------------------------------+ - -Logic Cheatsheet ----------------- - - .. container:: paragraph - - Examples given here use Javascript (if not stated otherwise), - other execution environments will be similar. +.. container:: paragraph + + These languages were chosen because the scripts can be compiled into Java bytecode at runtime and then efficiently + executed natively in the JVM. Task Logic an also be written directly in Java but needs to be compiled, with the + resulting classes added to the classpath. There are also a number of other Task Logic types (e.g. Fuzzy Logic), but + these are not supported as yet. This guide will focus on the scripted Task Logic approaches, with MVEL and JavaScript + being our favorite languages. In particular this guide will focus on the Apex aspects of the scripts. However, this + guide does not attempt to teach you about the scripting languages themselves …​ that is up to you! + +.. tip:: + JVM-based scripting languages For more more information on scripting for the Java platform see: + https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/index.html + +.. note:: + What do Tasks do? The function of an Apex Task is to provide the logic that can be executed for an Apex State as one + of the steps in an Apex Policy. Each task receives some *incoming fields*, executes some logic (e.g: make a decision + based on *shared state* or *context*, *incoming fields*, *external context*, etc.), perhaps set some *shared state* + or *context* and then emits *outgoing fields*. The state that uses the task is responsible for extracting the + *incoming fields* from the state input event. The state also has an *output mapper* associated with the task, and + this *output mapper* is responsible for mapping the *outgoing fields* from the task into an appropriate output event + for the state. + +.. container:: paragraph + + First lets start with a sample task, drawn from the "My First Apex Policy" example: The task "MorningBoozeCheck" + from the "My First Apex Policy" example is available in both MVEL and JavaScript: + +.. container:: listingblock + + .. container:: title + + Javascript code for the ``MorningBoozeCheck`` task + + .. container:: content + + .. code:: javascript + :number-lines: + + /* + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * Modifications Copyright (C) 2020 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + + executor.logger.info("Task Execution: '"+executor.subject.id+"'. Input Fields: '"+executor.inFields+"'"); + + executor.outFields.put("amount" , executor.inFields.get("amount")); + executor.outFields.put("assistant_ID", executor.inFields.get("assistant_ID")); + executor.outFields.put("notes" , executor.inFields.get("notes")); + executor.outFields.put("quantity" , executor.inFields.get("quantity")); + executor.outFields.put("branch_ID" , executor.inFields.get("branch_ID")); + executor.outFields.put("item_ID" , executor.inFields.get("item_ID")); + executor.outFields.put("time" , executor.inFields.get("time")); + executor.outFields.put("sale_ID" , executor.inFields.get("sale_ID")); + + item_id = executor.inFields.get("item_ID"); + + //All times in this script are in GMT/UTC since the policy and events assume time is in GMT. + var timenow_gmt = new Date(Number(executor.inFields.get("time"))); + + var midnight_gmt = new Date(Number(executor.inFields.get("time"))); + midnight_gmt.setUTCHours(0,0,0,0); + + var eleven30_gmt = new Date(Number(executor.inFields.get("time"))); + eleven30_gmt.setUTCHours(11,30,0,0); + + var timeformatter = new java.text.SimpleDateFormat("HH:mm:ss z"); + + var itemisalcohol = false; + if(item_id != null && item_id >=1000 && item_id < 2000) + itemisalcohol = true; + + if( itemisalcohol + && timenow_gmt.getTime() >= midnight_gmt.getTime() + && timenow_gmt.getTime() < eleven30_gmt.getTime()) { + + executor.outFields.put("authorised", false); + executor.outFields.put("message", "Sale not authorised by policy task " + + executor.subject.taskName+ " for time " + timeformatter.format(timenow_gmt.getTime()) + + ". Alcohol can not be sold between " + timeformatter.format(midnight_gmt.getTime()) + + " and " + timeformatter.format(eleven30_gmt.getTime())); + } + else{ + executor.outFields.put("authorised", true); + executor.outFields.put("message", "Sale authorised by policy task " + + executor.subject.taskName + " for time "+timeformatter.format(timenow_gmt.getTime())); + } + + /* + This task checks if a sale request is for an item that is an alcoholic drink. + If the local time is between 00:00:00 GMT and 11:30:00 GMT then the sale is not + authorised. Otherwise the sale is authorised. + In this implementation we assume that items with item_ID value between 1000 and + 2000 are all alcoholic drinks :-) + */ + + true; + +.. container:: listingblock + + .. container:: title + + MVEL code for the ``MorningBoozeCheck`` task + + .. container:: content + + .. code:: javascript + :number-lines: + + /* + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * Modifications Copyright (C) 2020 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + import java.util.Date; + import java.util.Calendar; + import java.util.TimeZone; + import java.text.SimpleDateFormat; + + logger.info("Task Execution: '"+subject.id+"'. Input Fields: '"+inFields+"'"); + + outFields.put("amount" , inFields.get("amount")); + outFields.put("assistant_ID", inFields.get("assistant_ID")); + outFields.put("notes" , inFields.get("notes")); + outFields.put("quantity" , inFields.get("quantity")); + outFields.put("branch_ID" , inFields.get("branch_ID")); + outFields.put("item_ID" , inFields.get("item_ID")); + outFields.put("time" , inFields.get("time")); + outFields.put("sale_ID" , inFields.get("sale_ID")); + + item_id = inFields.get("item_ID"); + + //The events used later to test this task use GMT timezone! + gmt = TimeZone.getTimeZone("GMT"); + timenow = Calendar.getInstance(gmt); + df = new SimpleDateFormat("HH:mm:ss z"); + df.setTimeZone(gmt); + timenow.setTimeInMillis(inFields.get("time")); + + midnight = timenow.clone(); + midnight.set( + timenow.get(Calendar.YEAR),timenow.get(Calendar.MONTH), + timenow.get(Calendar.DATE),0,0,0); + eleven30 = timenow.clone(); + eleven30.set( + timenow.get(Calendar.YEAR),timenow.get(Calendar.MONTH), + timenow.get(Calendar.DATE),11,30,0); + + itemisalcohol = false; + if(item_id != null && item_id >=1000 && item_id < 2000) + itemisalcohol = true; + + if( itemisalcohol + && timenow.after(midnight) && timenow.before(eleven30)){ + outFields.put("authorised", false); + outFields.put("message", "Sale not authorised by policy task "+subject.taskName+ + " for time "+df.format(timenow.getTime())+ + ". Alcohol can not be sold between "+df.format(midnight.getTime())+ + " and "+df.format(eleven30.getTime())); + return true; + } + else{ + outFields.put("authorised", true); + outFields.put("message", "Sale authorised by policy task "+subject.taskName+ + " for time "+df.format(timenow.getTime())); + return true; + } + + /* + This task checks if a sale request is for an item that is an alcoholic drink. + If the local time is between 00:00:00 GMT and 11:30:00 GMT then the sale is not + authorised. Otherwise the sale is authorised. + In this implementation we assume that items with item_ID value between 1000 and + 2000 are all alcoholic drinks :-) + */ + +.. container:: paragraph + + The role of the task in this simple example is to copy the values in the incoming fields into the outgoing + fields, then examine the values in some incoming fields (``item_id`` and ``time``), then set the values in some + other outgoing fields (``authorised`` and ``message``). + +.. container:: paragraph + + Both MVEL and JavaScript like most JVM-based scripting languages can use standard Java libraries to perform + complex tasks. Towards the top of the scripts you will see how to import Java classes and packages to be used + directly in the logic. Another thing to notice is that Task Logic should return a ``java.lang.Boolean`` value + ``true`` if the logic executed correctly. If the logic fails for some reason then ``false`` can be returned, but + this will cause the policy invoking this task will fail and exit. + +.. note:: + How to return a value from task logic + Some languages explicitly support returning values from the script (e.g. MVEL and JRuby) using an explicit + return statement (e.g. ``return true``), other languages do not (e.g. Jython). For + languages that do not support the ``return`` statement, a special field called ``returnValue`` must be + created to hold the result of the task logic operation (i.e. assign a ``java.lang.Boolean`` + value to the ``returnValue`` field before completing the task). + Also, in MVEL if there is no explicit return statement then the return value of the last executed statement will + return (e.g. the statement a=(1+2) will return the value 3). + + For Javascript, the last statement of a script must be a statement that evaluates to *true* or *false*, indicating + whether the script executed correctly or not. In the case where the script always executes to compeletion + sucessfully, simply add a last line with the statement *true'*. In cases where success or failure is assessed in the + script, create a boolean + local variable with a name such as ``returnvalue``. In the execution of the script, set ``returnValue`` to be ``true`` + or ``false`` as appropriate. The last line of the scritp tehn should simply be ``returnValue;``, which returns the + value of ``returnValue``. + +.. container:: paragraph + + Besides these imported classes and normal language features Apex provides some natively available parameters + and functions that can be used directly. At run-time these parameters are populated by the Apex execution + environment and made natively available to logic scripts each time the logic script is invoked. (These can be + accessed using the ``executor`` keyword for most languages, or can be accessed directly without the + ``executor`` keyword in MVEL): + +Table 1. The ``executor`` Fields / Methods + + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + | Name | Type | Java type | Description | + +=====================================================+==========================================================================+===============================+==================================================================================+ + | inFields | Fields | java.util.Map |The incoming task fields, implemented as a standard Java (unmodifiable) Map | + | | | | | + | | | |**Example:** | + | | | | | + | | | |.. code:: javascript | + | | | | | + | | | | executor.logger.debug("Incoming fields: " +executor.inFields.entrySet()); | + | | | | var item_id = executor.incomingFields["item_ID"]; | + | | | | if (item_id >=1000) { ... } | + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + | outFields | Fields | java.util.Map |The outgoing task fields. This is implemented as a standard initially empty Java | + | | | |(modifiable) Map. To create a new schema-compliant instance of a field object | + | | | |see the utility method subject.getOutFieldSchemaHelper() below | + | | | | | + | | | |**Example:** | + | | | | | + | | | |.. code:: javascript | + | | | | | + | | | | executor.outFields["authorised"] = false; | + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + | logger | Logger | org.slf4j.ext.XLogger |A helpful logger | + | | | | | + | | | |**Example:** | + | | | | | + | | | |.. code:: javascript | + | | | | | + | | | | executor.logger.info("Executing task: " +executor.subject.id); | + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + | TRUE/FALSE | boolean | java.lang.Boolean |2 helpful constants. These are useful to retrieve correct return values for the | + | | | |task logic | + | | | | | + | | | |**Example:** | + | | | | | + | | | |.. code:: javascript | + | | | | | + | | | | var returnValue = executor.isTrue; | + | | | | var returnValueType = Java.type("java.lang.Boolean"); | + | | | | var returnValue = new returnValueType(true); | + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + | subject | Task | TaskFacade |This provides some useful information about the task that contains this task | + | | | |logic. This object has some useful fields and methods : | + | | | | | + | | | |.. container:: ulist | + | | | | | + | | | | - **AxTask task** to get access to the full task definition of the host task | + | | | | | + | | | | - **String getTaskName()** to get the name of the host task | + | | | | | + | | | | - **String getId()** to get the ID of the host task | + | | | | | + | | | | - **SchemaHelper getInFieldSchemaHelper( String fieldName )** to | + | | | | get a ``SchemaHelper`` helper object to manipulate incoming | + | | | | task fields in a schema-aware manner | + | | | | | + | | | | - **SchemaHelper getOutFieldSchemaHelper( String fieldName )** to | + | | | | get a ``SchemaHelper`` helper object to manipulate outgoing | + | | | | task fields in a schema-aware manner, e.g. to instantiate new | + | | | | schema-compliant field objects to populate the | + | | | | ``executor.outFields`` outgoing fields map | + | | | | | + | | | |**Example:** | + | | | | | + | | | |.. code:: javascript | + | | | | | + | | | | executor.logger.info("Task name: " + executor.subject.getTaskName()); | + | | | | executor.logger.info("Task id: " + executor.subject.getId()); | + | | | | executor.logger.info("Task inputs definitions: " | + | | | | + "executor.subject.task.getInputFieldSet()); | + | | | | executor.logger.info("Task outputs definitions: " | + | | | | + "executor.subject.task.getOutputFieldSet()); | + | | | | executor.outFields["authorised"] = executor.subject | + | | | | .getOutFieldSchemaHelper("authorised").createNewInstance("false"); | + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + | ContextAlbum getContextAlbum(String ctxtAlbumName ) |A utility method to retrieve a ``ContextAlbum`` for use in the task. | | | + | |This is how you access the context used by the task. The returned | | | + | |``ContextAlbum`` implements the ``java.util.Map `` | | | + | |interface to get and set context as appropriate. The returned | | | + | |``ContextAlbum`` also has methods to lock context albums, get | | | + | |information about the schema of the items to be stored in a context | | | + | |album, and get a ``SchemaHelper`` to manipulate context album items. How | | | + | |to define and use context in a task is described in the Apex | | | + | |Programmer’s Guide and in the My First Apex Policy guide. | | | + | | | | | + | |**Example:** | | | + | | | | | + | |.. code:: javascript | | | + | | | | | + | | var bkey = executor.inFields.get("branch_ID"); | | | + | | var cnts = executor.getContextMap("BranchCounts"); | | | + | | cnts.lockForWriting(bkey); | | | + | | cnts.put(bkey, cnts.get(bkey) + 1); | | | + | | cnts.unlockForWriting(bkey); | | | + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + +Writing APEX Task Selection Logic +================================= + +.. container:: paragraph + + The function of Task Selection Logic is to choose which task should be executed for an Apex State as one of + the steps in an Apex Policy. Since each state must define a default task there is no need for Task Selection + Logic unless the state uses more than one task. This logic can be specified in a number of ways, exploiting + Apex’s plug-in architecture to support a range of logic executors. In Apex scripted Task Selection Logic can be + written in any of these languages: + +.. container:: ulist + + - ```MVEL`` `__, + + - ```JavaScript`` `__, + + - ```JRuby`` `__ or + + - ```Jython`` `__. + +.. container:: paragraph + + These languages were chosen because the scripts can be compiled into Java bytecode at runtime and then + efficiently executed natively in the JVM. Task Selection Logic an also be written directly in Java but needs to + be compiled, with the resulting classes added to the classpath. There are also a number of other Task Selection + Logic types but these are not supported as yet. This guide will focus on the scripted Task Selection Logic + approaches, with MVEL and JavaScript being our favorite languages. In particular this guide will focus on the + Apex aspects of the scripts. However, this guide does not attempt to teach you about the scripting languages + themselves …​ that is up to you! + +.. tip:: + JVM-based scripting languages + For more more information on Scripting for the Java platform see: + https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/index.html + +.. note:: + What does Task Selection Logic do? + When an Apex state references multiple tasks, there must be a way to dynamically decide + which task should be chosen and executed. This can depend on the many factors, e.g. the + *incoming event for the state*, *shared state* or *context*, *external context*, + etc.. This is the function of a state’s Task Selection Logic. Obviously, if there is + only one task then Task only one task then Task Selection Logic is not needed. + Each state must also select one of the tasks a the *default state*. If the Task + Selection Logic is unable to select an appropriate task, then it should select the + *default task*. Once the task has been selected the Apex Engine will then execute that task. + +.. container:: paragraph + + First lets start with some simple Task Selection Logic, drawn from the "My First Apex Policy" example: The Task + Selection Logic from the "My First Apex Policy" example is specified in JavaScript here: + +.. container:: listingblock + + .. container:: title + + Javascript code for the "My First Policy" Task Selection Logic + + .. container:: content + + .. code:: javascript + + /* + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * Modifications Copyright (C) 2020 Nordix Foundation. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + + executor.logger.info("Task Selection Execution: '"+executor.subject.id+ + "'. Input Event: '"+executor.inFields+"'"); + + branchid = executor.inFields.get("branch_ID"); + taskorig = executor.subject.getTaskKey("MorningBoozeCheck"); + taskalt = executor.subject.getTaskKey("MorningBoozeCheckAlt1"); + taskdef = executor.subject.getDefaultTaskKey(); + + if(branchid >=0 && branchid <1000){ + taskorig.copyTo(executor.selectedTask); + } + else if (branchid >=1000 && branchid <2000){ + taskalt.copyTo(executor.selectedTask); + } + else{ + taskdef.copyTo(executor.selectedTask); + } + + /* + This task selection logic selects task "MorningBoozeCheck" for branches with + 0<=branch_ID<1000 and selects task "MorningBoozeCheckAlt1" for branches with + 1000<=branch_ID<2000. Otherwise the default task is selected. + In this case the default task is also "MorningBoozeCheck" + */ + + true; + +.. container:: paragraph + + The role of the Task Selection Logic in this simple example is to examine the value in one incoming field + (``branchid``), then depending on that field’s value set the value for the selected task to the appropriate task + (``MorningBoozeCheck``, ``MorningBoozeCheckAlt1``, or the default task). + +.. container:: paragraph + + Another thing to notice is that Task Selection Logic should return a ``java.lang.Boolean`` value ``true`` if + the logic executed correctly. If the logic fails for some reason then ``false`` can be returned, but this will + cause the policy invoking this task will fail and exit. + +.. note:: + How to return a value from Task Selection Logic + Some languages explicitly support returning values from the script (e.g. MVEL and + JRuby) using an explicit return statement (e.g. ``return true``), other languages do not (e.g. + JavaScript and Jython). For languages that do not support the ``return`` statement, a special field called + ``returnValue`` must be created to hold the result of the task logic operation (i.e. assign a ``java.lang.Boolean`` + value to the ``returnValue`` field before completing the task). + Also, in MVEL if there is not explicit return statement then the return value of the last executed statement will + return (e.g. the statement a=(1+2) will return the value 3). + +.. container:: paragraph + + Each of the scripting languages used in Apex can import and use standard Java libraries to perform complex tasks. + Besides imported classes and normal language features Apex provides some natively available parameters and functions + that can be used directly. At run-time these parameters are populated by the Apex execution environment and made + natively available to logic scripts each time the logic script is invoked. (These can be accessed using the + ``executor`` keyword for most languages, or can be accessed directly without the ``executor`` keyword in MVEL): + +Table 2. The ``executor`` Fields / Methods + +-----------------------------------+------------------------------------+ + | Unix, Cygwin | Windows | + +===================================+====================================+ + |.. container:: content |.. container:: content | + | | | + | .. code:: bash | .. code:: bash | + | :number-lines: | :number-lines: | + | | | + | >c: | # cd /usr/local/src/apex-pdp | + | >cd \dev\apex | # mvn clean install -DskipTests | + | >mvn clean install -DskipTests | | + +-----------------------------------+------------------------------------+ + + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + | Name | Type | Java type | Description | + +=====================================================+==========================================================================+===============================+==================================================================================+ + | inFields | Fields | java.util.Map | All fields in the state’s incoming event. This is implemented as a standard Java | + | | | | Java (unmodifiable) Map | + | | | | | + | | | | **Example:** | + | | | | | + | | | | .. code:: javascript | + | | | | | + | | | | executor.logger.debug("Incoming fields: " + executor.inFields.entrySet()); | + | | | | var item_id = executor.incomingFields["item_ID"]; | + | | | | if (item_id >=1000) { ... } | + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + | outFields | Fields | java.util.Map | The outgoing task fields. This is implemented as a standard initially empty Java | + | | | | (modifiable) Map. To create a new schema-compliant instance of a field object | + | | | | see the utility method subject.getOutFieldSchemaHelper() below | + | | | | | + | | | | **Example:** | + | | | | | + | | | | .. code:: javascript | + | | | | | + | | | | executor.outFields["authorised"] = false; | + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + | logger | Logger | org.slf4j.ext.XLogger | A helpful logger | + | | | | | + | | | | **Example:** | + | | | | | + | | | | .. code:: javascript | + | | | | | + | | | | executor.logger.info("Executing task: " | + | | | | +executor.subject.id); | + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + | TRUE/FALSE | boolean | java.lang.Boolean | 2 helpful constants. These are useful to retrieve correct return values for the | + | | | | task logic | + | | | | | + | | | | **Example:** | + | | | | | + | | | | .. code:: javascript | + | | | | | + | | | | var returnValue = executor.isTrue; | + | | | | var returnValueType = Java.type("java.lang.Boolean"); | + | | | | var returnValue = new returnValueType(true); | + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + | subject | Task | TaskFacade | This provides some useful information about the task that contains this task | + | | | | logic. This object has some useful fields and methods : | + | | | | | + | | | | .. container:: ulist | + | | | | | + | | | | - **AxTask task** to get access to the full task definition of the host task | + | | | | | + | | | | - **String getTaskName()** to get the name of the host task | + | | | | | + | | | | - **String getId()** to get the ID of the host task | + | | | | | + | | | | - **SchemaHelper getInFieldSchemaHelper( String fieldName )** to | + | | | | get a ``SchemaHelper`` helper object to manipulate incoming | + | | | | task fields in a schema-aware manner | + | | | | | + | | | | - **SchemaHelper getOutFieldSchemaHelper( String fieldName )** to | + | | | | get a ``SchemaHelper`` helper object to manipulate outgoing | + | | | | task fields in a schema-aware manner, e.g. to instantiate new | + | | | | schema-compliant field objects to populate the | + | | | | ``executor.outFields`` outgoing fields map | + | | | | | + | | | | **Example:** | + | | | | | + | | | | .. code:: javascript | + | | | | | + | | | | executor.logger.info("Task name: " + executor.subject.getTaskName()); | + | | | | executor.logger.info("Task id: " + executor.subject.getId()); | + | | | | executor.logger.info("Task inputs definitions: " | + | | | | + "executor.subject.task.getInputFieldSet()); | + | | | | executor.logger.info("Task outputs definitions: " | + | | | | + "executor.subject.task.getOutputFieldSet()); | + | | | | executor.outFields["authorised"] = executor.subject | + | | | | .getOutFieldSchemaHelper("authorised") | + | | | | .createNewInstance("false"); | + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + | parameters | Fields | java.util.Map | All parameters in the current task. This is implemented as a standard Java Map. | + | | | | | + | | | | **Example:** | + | | | | | + | | | | .. code:: javascript | + | | | | | + | | | | executor.parameters.get("ParameterKey1")) | + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + | ContextAlbum getContextAlbum(String ctxtAlbumName ) | A utility method to retrieve a ``ContextAlbum`` for use in the task. | | | + | | This is how you access the context used by the task. The returned | | | + | | ``ContextAlbum`` implements the ``java.util.Map `` | | | + | | interface to get and set context as appropriate. The returned | | | + | | ``ContextAlbum`` also has methods to lock context albums, get | | | + | | information about the schema of the items to be stored in a context | | | + | | album, and get a ``SchemaHelper`` to manipulate context album items. How | | | + | | to define and use context in a task is described in the Apex | | | + | | Programmer’s Guide and in the My First Apex Policy guide. | | | + | | | | | + | | **Example:** | | | + | | | | | + | | .. code:: javascript | | | + | | | | | + | | var bkey = executor.inFields.get("branch_ID"); | | | + | | var cnts = executor.getContextMap("BranchCounts"); | | | + | | cnts.lockForWriting(bkey); | | | + | | cnts.put(bkey, cnts.get(bkey) + 1); | | | + | | cnts.unlockForWriting(bkey); | | | + +-----------------------------------------------------+--------------------------------------------------------------------------+-------------------------------+----------------------------------------------------------------------------------+ + +Logic Cheat Sheet +================= + +.. container:: paragraph + + Examples given here use Javascript (if not stated otherwise), other execution environments will be similar. Finish Logic with Success or Error -################################## +---------------------------------- - .. container:: paragraph +.. container:: paragraph - To finish logic, i.e. return to APEX, with success use the - following line close to the end of the logic. + To finish logic, i.e. return to APEX, with success use the following line close to the end of the logic. - .. container:: listingblock +.. container:: listingblock - .. container:: title + .. container:: title - JS Success + JS Success - .. container:: content + .. container:: content - .. code:: javascript + .. code:: javascript - true; + true; - .. container:: paragraph +.. container:: paragraph - To notify a problem, finish with an error. + To notify a problem, finish with an error. - .. container:: listingblock + .. container:: listingblock - .. container:: title + .. container:: title - JS Fail + JS Fail - .. container:: content + .. container:: content - .. code:: javascript + .. code:: javascript - false; + false; Logic Logging -############# +------------- - .. container:: paragraph +.. container:: paragraph - Logging can be made easy using a local variable for the logger. - Line 1 below does that. Then we start with a trace log with the - task (or task logic) identifier followed by the infields. + Logging can be made easy using a local variable for the logger. Line 1 below does that. Then we start + with a trace log with the task (or task logic) identifier followed by the infields. - .. container:: listingblock +.. container:: listingblock - .. container:: title + .. container:: title - JS Logging + JS Logging - .. container:: content + .. container:: content - .. code:: javascript + .. code:: javascript - var logger = executor.logger; - logger.trace("start: " + executor.subject.id); - logger.trace("-- infields: " + executor.inFields); + var logger = executor.logger; + logger.trace("start: " + executor.subject.id); + logger.trace("-- infields: " + executor.inFields); - .. container:: paragraph +.. container:: paragraph - For larger logging blocks you can use the standard logging API - to detect log levels, for instance: + For larger logging blocks you can use the standard logging API to detect log levels, for instance: - .. container:: listingblock + .. container:: listingblock - .. container:: title + .. container:: title - JS Logging Blocks + JS Logging Blocks - .. container:: content + .. container:: content - .. code:: javascript + .. code:: javascript - if(logger.isTraceEnabled()){ - // trace logging block here - } + if(logger.isTraceEnabled()){ + // trace logging block here + } - .. container:: paragraph +.. container:: paragraph - Note: the shown logger here logs to - ``org.onap.policy.apex.executionlogging``. The behavior of the - actual logging can be specified in the - ``$APEX_HOME/etc/logback.xml``. + Note: the shown logger here logs to ``org.onap.policy.apex.executionlogging``. The behavior of the actual logging can + be specified in the ``$APEX_HOME/etc/logback.xml``. - .. container:: paragraph +.. container:: paragraph - If you want to log into the APEX root logger (which is - sometimes necessary to report serious logic errors to the top), - then import the required class and use this logger. + If you want to log into the APEX root logger (which is sometimes necessary to report serious logic errors to the top), + then import the required class and use this logger. - .. container:: listingblock +.. container:: listingblock - .. container:: title + .. container:: title - JS Root Logger + JS Root Logger - .. container:: content + .. container:: content - .. code:: javascript + .. code:: javascript - importClass(org.slf4j.LoggerFactory); - var rootLogger = LoggerFactory.getLogger(logger.ROOT_LOGGER_NAME); - - rootLogger.error("Serious error in logic detected: " + executor.subject.id); + var rootLogger = LoggerFactory.getLogger(logger.ROOT_LOGGER_NAME); + rootLogger.error("Serious error in logic detected: " + executor.subject.id); Accessing TaskParameters -######################## +------------------------ - .. container:: paragraph +.. container:: paragraph - TaskParameters available in a Task can be accessed in the logic. - The parameters in each task are made available at the executor level. - This example assumes a parameter with key ``ParameterKey1``. + TaskParameters available in a Task can be accessed in the logic. The parameters in each task are made + available at the executor level. This example assumes a parameter with key ``ParameterKey1``. - .. container:: listingblock + .. container:: listingblock - .. container:: title + .. container:: title - JS TaskParameter value + JS TaskParameter value - .. container:: content + .. container:: content - .. code:: javascript + .. code:: javascript - executor.parameters.get("ParameterKey1")) + executor.parameters.get("ParameterKey1")) - .. container:: paragraph +.. container:: paragraph - Alternatively, the task parameters can also be accessed from the task object. + Alternatively, the task parameters can also be accessed from the task object. - .. container:: listingblock + .. container:: listingblock - .. container:: title + .. container:: title - JS TaskParameter value using task object + JS TaskParameter value using task object - .. container:: content + .. container:: content - .. code:: javascript + .. code:: javascript - executor.subject.task.getTaskParameters.get("ParameterKey1").getTaskParameterValue() + executor.subject.task.getTaskParameters.get("ParameterKey1").getTaskParameterValue() Local Variable for Infields -########################### +--------------------------- - .. container:: paragraph +.. container:: paragraph - It is a good idea to use local variables for ``infields``. This - avoids long code lines and policy evolution. The following - example assumes infields named ``nodeName`` and ``nodeAlias``. + It is a good idea to use local variables for ``infields``. This avoids long code lines and policy + evolution. The following example assumes infields named ``nodeName`` and ``nodeAlias``. - .. container:: listingblock + .. container:: listingblock - .. container:: title + .. container:: title - JS Infields Local Var + JS Infields Local Var - .. container:: content + .. container:: content - .. code:: javascript + .. code:: javascript - var ifNodeName = executor.inFields["nodeName"]; - var ifNodeAlias = executor.inFields["nodeAlias"]; + var ifNodeName = executor.inFields["nodeName"]; + var ifNodeAlias = executor.inFields["nodeAlias"]; Local Variable for Context Albums -################################# +--------------------------------- - .. container:: paragraph +.. container:: paragraph - Similar to the ``infields`` it is good practice to use local - variables for context albums as well. The following example - assumes that a task can access a context album - ``albumTopoNodes``. The second line gets a particular node from - this context album. + Similar to the ``infields`` it is good practice to use local variables for context albums as well. The + following example assumes that a task can access a context album ``albumTopoNodes``. The second line gets a + particular node from this context album. - .. container:: listingblock +.. container:: listingblock - .. container:: title + .. container:: title - JS Infields Local Var + JS Infields Local Var - .. container:: content + .. container:: content - .. code:: javascript + .. code:: javascript - var albumTopoNodes = executor.getContextAlbum("albumTopoNodes"); - var ctxtNode = albumTopoNodes.get(ifNodeName); + var albumTopoNodes = executor.getContextAlbum("albumTopoNodes"); + var ctxtNode = albumTopoNodes.get(ifNodeName); Set Outfields in Logic -###################### +---------------------- - .. container:: paragraph +.. container:: paragraph - The task logic needs to set outfields with content generated. - The exception are outfields that are a direct copy from an - infield of the same name, APEX does that autmatically. + The task logic needs to set outfields with content generated. The exception are outfields that are a + direct copy from an infield of the same name, APEX does that autmatically. - .. container:: listingblock +.. container:: listingblock - .. container:: title + .. container:: title - JS Set Outfields + JS Set Outfields - .. container:: content + .. container:: content - .. code:: javascript + .. code:: javascript - executor.outFields["report"] = "node ctxt :: added node " + ifNodeName; + executor.outFields["report"] = "node ctxt :: added node " + ifNodeName; Create a instance of an Outfield using Schemas -############################################## +---------------------------------------------- + +.. container:: paragraph - .. container:: paragraph + If an outfield is not an atomic type (string, integer, etc.) but uses a complex schema (with a Java or + Avro backend), APEX can help to create new instances. The ``executor`` provides a field called ``subject``, + which provides a schem helper with an API for this. The complete API of the schema helper is documented here: + `API Doc: SchemaHelper `__. - 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 `__. +.. container:: paragraph - .. container:: paragraph + If the backend is Java, then the Java class implementing the schema needs to be imported. - If the backend is Avro, then an import of the Avro schema - library is required: +.. container:: paragraph - .. container:: listingblock + The following example assumes an outfield ``situation``. The ``subject`` method ``getOutFieldSchemaHelper()`` is used + to create a new instance. - .. container:: title +.. container:: listingblock - JS Import Avro + .. container:: title - .. container:: content + JS Outfield Instance with Schema - .. code:: javascript + .. container:: content - importClass(org.apache.avro.generic.GenericData.Array); - importClass(org.apache.avro.generic.GenericRecord); - importClass(org.apache.avro.Schema); + .. code:: javascript - .. container:: paragraph + var situation = executor.subject.getOutFieldSchemaHelper("situation").createNewInstance(); - If the backend is Java, then the Java class implementing the - schema needs to be imported. +.. container:: paragraph - .. container:: paragraph + If the schema backend is Java, the new instance will be as implemented in the Java class. If the schema backend is + Avro, the new instance will have all fields from the Avro schema specification, but set to ``null``. So any entry here + needs to be done separately. For instance, the ``situation`` schema has a field ``problemID`` which we set. - The following example assumes an outfield ``situation``. The - ``subject`` method ``getOutFieldSchemaHelper()`` is used to - create a new instance. +.. container:: listingblock - .. container:: listingblock + .. container:: title - .. container:: title + JS Outfield Instance with Schema, set - JS Outfield Instance with Schema + .. container:: content - .. container:: content + .. code:: javascript - .. code:: javascript + situation.put("problemID", "my-problem"); - var situation = executor.subject.getOutFieldSchemaHelper("situation").createNewInstance(); +Create a instance of an Context Album entry using Schemas +--------------------------------------------------------- - .. container:: paragraph +.. container:: paragraph - If the schema backend is Java, the new instance will be as - implemented in the Java class. If the schema backend is Avro, - the new instance will have all fields from the Avro schema - specification, but set to ``null``. So any entry here needs to - be done separately. For instance, the ``situation`` schema has - a field ``problemID`` which we set. + Context album instances can be created using very similar to the outfields. Here, the schema helper + comes from the context album directly. The API of the schema helper is the same as for outfields, see + `API Doc: SchemaHelper `__. - .. container:: listingblock +.. container:: paragraph - .. container:: title + If the backend is Java, then the Java class implementing the schema needs to be imported. - JS Outfield Instance with Schema, set +.. container:: paragraph - .. container:: content + The following example creates a new instance of a context album instance named ``albumProblemMap``. - .. code:: javascript +.. container:: listingblock - situation.put("problemID", "my-problem"); + .. container:: title -Create a instance of an Context Album entry using Schemas -######################################################### + JS Outfield Instance with Schema + + .. container:: content - .. container:: paragraph + .. code:: javascript - Context album instances can be created using very similar to - the outfields. Here, the schema helper comes from the context - album directly. The API of the schema helper is the same as for - outfields, see `API Doc: - SchemaHelper `__. + var albumProblemMap = executor.getContextAlbum("albumProblemMap"); + var linkProblem = albumProblemMap.getSchemaHelper().createNewInstance(); - .. container:: paragraph +.. container:: paragraph - If the backend is Avro, then an import of the Avro schema - library is required: + This can of course be also done in a single call without the local variable for the context album. - .. container:: listingblock +.. container:: listingblock - .. container:: title + .. container:: title - JS Import Avro + JS Outfield Instance with Schema, one line - .. container:: content + .. container:: content - .. code:: javascript + .. code:: javascript - importClass(org.apache.avro.generic.GenericData.Array); - importClass(org.apache.avro.generic.GenericRecord); - importClass(org.apache.avro.Schema); + var linkProblem = executor.getContextAlbum("albumProblemMap").getSchemaHelper().createNewInstance(); - .. container:: paragraph +.. container:: paragraph - If the backend is Java, then the Java class implementing the - schema needs to be imported. + If the schema backend is Java, the new instance will be as implemented in the Java class. If the schema backend is + Avro, the new instance will have all fields from the Avro schema specification, but set to ``null``. So any entry here + needs to be done separately (see above in outfields for an example). - .. container:: paragraph +Enumerates +---------- - The following example creates a new instance of a context album - instance named ``albumProblemMap``. +.. container:: paragraph - .. container:: listingblock + When dealing with enumerates (Avro or Java defined), it is sometimes and in some execution + environments necessary to convert them to a string. For example, assume an Avro enumerate schema as: - .. container:: title +.. container:: listingblock - JS Outfield Instance with Schema + .. container:: title - .. container:: content + Avro Enumerate Schema - .. code:: javascript + .. container:: content - var albumProblemMap = executor.getContextAlbum("albumProblemMap"); - var linkProblem = albumProblemMap.getSchemaHelper().createNewInstance(); + .. code:: javascript - .. container:: paragraph + { + "type": "enum", "name": "Status", "symbols" : [ + "UP", "DOWN" + ] + } - This can of course be also done in a single call without the - local variable for the context album. +.. container:: paragraph - .. container:: listingblock + Using a switch over a field initialized with this enumerate in Javascript will fail. Instead, use the ``toString`` method, for example: - .. container:: title +.. container:: listingblock - JS Outfield Instance with Schema, one line + .. container:: title - .. container:: content + JS Outfield Instance with Schema, one line - .. code:: javascript + .. container:: content - var linkProblem = executor.getContextAlbum("albumProblemMap").getSchemaHelper().createNewInstance(); + .. code:: javascript - .. container:: paragraph + var switchTest = executor.inFields["status"]; switch(switchTest.toString()){ + case "UP": ...; break; case "DOWN": ...; break; default: ...; + } - If the schema backend is Java, the new instance will be as - implemented in the Java class. If the schema backend is Avro, - the new instance will have all fields from the Avro schema - specification, but set to ``null``. So any entry here needs to - be done separately (see above in outfields for an example). +MVEL Initialize Outfields First! +-------------------------------- -Enumerates -########## +.. container:: paragraph - .. container:: paragraph + In MVEL, we observed a problem when accessing (setting) outfields without a prior access to them. So + in any MVEL task logic, before setting any outfield, simply do a get (with any string), to load the outfields + into the MVEL cache. - When dealing with enumerates (Avro or Java defined), it is - sometimes and in some execution environments necessary to - convert them to a string. For example, assume an Avro enumerate - schema as: +.. container:: listingblock - .. container:: listingblock + .. container:: title - .. container:: title + MVEL Outfield Initialization - Avro Enumerate Schema + .. container:: content - .. container:: content + .. code:: javascript - .. code:: javascript + outFields.get("initialize outfields"); - { - "type": "enum", - "name": "Status", - "symbols" : [ - "UP", - "DOWN" - ] - } +Using Java in Scripting Logic +----------------------------- - .. container:: paragraph +.. container:: paragraph - Using a switch over a field initialized with this enumerate in - Javascript will fail. Instead, use the ``toString`` method, for - example: + Since APEX executes the logic inside a JVM, most scripting languages provide access to all standard + Java classes. Simply add an import for the required class and then use it as in actual Java. - .. container:: listingblock +.. container:: paragraph - .. container:: title + The following example imports ``java.util.arraylist`` into a Javascript logic, and then creates a new + list. - JS Outfield Instance with Schema, one line +.. container:: listingblock - .. container:: content + .. container:: title - .. code:: javascript + JS Import ArrayList - var switchTest = executor.inFields["status"]; - switch(switchTest.toString()){ - case "UP": ...; break; - case "DOWN": ...; break; - default: ...; - } + .. container:: content -MVEL Initialize Outfields First! -################################ + .. code:: javascript + + var myList = new ArrayList(); - .. container:: paragraph +Converting Javascript scripts from Nashorn to Rhino dialects +------------------------------------------------------------ - In MVEL, we observed a problem when accessing (setting) - outfields without a prior access to them. So in any MVEL task - logic, before setting any outfield, simply do a get (with any - string), to load the outfields into the MVEL cache. +The Nashorn Javascript engine was removed from Java in the Java 11 release. Java 11 was introduced into +the Policy Framework in the Frankfurt release, so from Frankfurt on, APEX Javascript scripts use the Rhino +Javascript engine and scripts must be in the Rhino dialect. - .. container:: listingblock +There are some minor but important differences between the dialects that users should be aware of so +that they can convert their scripts into the Rhino dialect. - .. container:: title +Return Values +^^^^^^^^^^^^^ - MVEL Outfield Initialization +APEX scripts must always return a value of ``true`` indicating that the script executed correctly or ``false`` +indicating that there was an error in script execution. - .. container:: content +*Pre Frankfurt* - .. code:: javascript +In Nashorn dialect scripts, the user had to create a special variable called ``returnValue`` and set the value of +that variable to be the return value for the script. - outFields.get("initialize outfields"); +*Frankfurt and Later* -Using Java in Scripting Logic -############################# +In Rhino dialect scripts, the return value of the script is the logical result of the last statement. Therefore the +last line of the script must evaluate to either ``true`` or ``false``. + +.. container:: listingblock + + .. container:: title + + JS Rhino script last executed line examples + + .. container:: content + + .. code:: javascript + + true; + + returnValue; // Where returnValue is assigned earlier in the script + + someValue == 1; // Where the value of someValue is assigned earlier in the script + +return statement +^^^^^^^^^^^^^^^^ + +The ``return`` statement is not supported from the main script called in the Rhino interpreter. + +*Pre Frankfurt* + +In Nashorn dialect scripts, the user could return a value of ``true`` or ``false`` at any point in their script. + +.. container:: listingblock + + .. container:: title + + JS Nashorn main script returning ``true`` and ``false`` + + .. container:: content + + .. code:: javascript + + var n; + + // some code assigns n a value + + if (n < 2) { + return false; + } else { + return true; + } + +*Frankfurt and Later* + +In Rhino dialect scripts, the ``return`` statement cannot be used in the main method, but it can still be used in +functions. If you want to have a ``return`` statement in your code prior to the last statement, encapsulate your code +in a function. + +.. container:: listingblock + + .. container:: title + + JS Rhino script with ``return`` statements in a function + + .. container:: content + + .. code:: javascript + + someFunction(); + + function someFunction() { + var n; + + // some code assigns n a value + + if (n < 2) { + return false; + } else { + return true; + } + } + +Compatibility Script +^^^^^^^^^^^^^^^^^^^^ + +For Nashorn, the user had to call a compatibility script at the beginning of their Javascript script. This is not +required in Rhino. + +*Pre Frankfurt* + +In Nashorn dialect scripts, the compatibility script must be loaded. + +.. container:: listingblock + + .. container:: title + + Nashorn compatability script loading + + .. container:: content + + .. code:: javascript + + load("nashorn:mozilla_compat.js"); + +*Frankfurt and Later* + +Not required. + +Import of Java classes +^^^^^^^^^^^^^^^^^^^^^^ + +For Nashorn, the user had explicitly import all the Java packages and classes they wished to use in their Javascript +script. In Rhino, all Java classes on the classpath are available for use. + +*Pre Frankfurt* + +In Nashorn dialect scripts, Java classes must be imported. + +.. container:: listingblock + + .. container:: title + + Importation of Java packages and classes + + .. container:: content + + .. code:: javascript + + importPackage(java.text); + importClass(java.text.SimpleDateFormat); + +*Frankfurt and Later* + +Not required. + +Using Java Classes and Objects as Variables +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Setting a Javascript variable to hold a Java class or a Java object is more straightforward in Rhino than it is in +Nashorn. The examples below show how to instantiate a Javascript variable as a Java class and how to use that variable +to create an instance of the Java class in another Javascript variable in both dialects. + + +*Pre Frankfurt* + +.. container:: listingblock - .. container:: paragraph + .. container:: title - Since APEX executes the logic inside a JVM, most scripting - languages provide access to all standard Java classes. Simply - add an import for the required class and then use it as in - actual Java. + Create Javascript variables to hold a Java class and instance - .. container:: paragraph + .. container:: content - The following example imports ``java.util.arraylist`` into a - Javascript logic, and then creates a new list. + .. code:: javascript - .. container:: listingblock + var webClientClass = Java.type("org.onap.policy.apex.examples.bbs.WebClient"); + var webClientObject = new webClientClass(); - .. container:: title +*Frankfurt and Later* - JS Import ArrayList +.. container:: listingblock - .. container:: content + .. container:: title - .. code:: javascript + Create Javascript variables to hold a Java class and instance - importClass(java.util.ArrayList); - var myList = new ArrayList(); + .. container:: content + .. code:: javascript -.. container:: - :name: footer + var webClientClass = org.onap.policy.apex.examples.bbs.WebClient; + var webClientObject = new webClientClass(); - .. container:: - :name: footer-text +Equal Value and Equal Type operator ``===`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 2.3.0-SNAPSHOT - Last updated 2020-03-16 16:04:24 GMT +The *Equal Value and Equal Type* operator ``===`` is not supported in Rhino. Developers must use the Equal To +operator ``==`` instead. To check types, they may need to explicitly find and check the type of the variables +they are using. .. |APEX Policy Matrix| image:: images/apex-intro/ApexPolicyMatrix.png .. |APEX Policy Model for Execution| image:: images/apex-policy-model/UmlPolicyModels.png -- cgit 1.2.3-korg