1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
//
// ============LICENSE_START=======================================================
// Copyright (C) 2016-2018 Ericsson. All rights reserved.
// ================================================================================
// This file is licensed under the CREATIVE COMMONS ATTRIBUTION 4.0 INTERNATIONAL LICENSE
// Full license text at https://creativecommons.org/licenses/by/4.0/legalcode
//
// SPDX-License-Identifier: CC-BY-4.0
// ============LICENSE_END=========================================================
//
// @author Sven van der Meer (sven.van.der.meer@ericsson.com)
//
== Writing APEX Task Logic
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:
* https://en.wikipedia.org/wiki/MVEL[`MVEL`],
* https://en.wikipedia.org/wiki/JavaScript[`JavaScript`],
* https://en.wikipedia.org/wiki/JRuby[`JRuby`] or
* https://en.wikipedia.org/wiki/Jython[`Jython`].
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.
====
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:
.Javascript code for the `MorningBoozeCheck` task
[source,javascript,options="nowrap"]
----
include::{adsite-examples-myfirstpolicy-dir}/main/resources/examples/models/MyFirstPolicy/1/MorningBoozeCheck.js[]
----
.MVEL code for the `MorningBoozeCheck` task
[source,java,options="nowrap"]
----
include::{adsite-examples-myfirstpolicy-dir}/main/resources/examples/models/MyFirstPolicy/1/MorningBoozeCheck.mvel[]
----
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`).
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).
====
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):
.The `executor` Fields / Methods
[width="100%",cols="10l,10d,30m,40a",options="header"]
|====================
|Name | Type | Java type | Description
|inFields | Fields | java.util.Map <String,Object> |
The incoming task fields. This is implemented as a standard Java (unmodifiable) Map.
2+| 2+<a|
*Example:*
[source,javascript,options="nowrap"]
----
executor.logger.debug("Incoming fields: "
+executor.inFields.entrySet());
var item_id = executor.incomingFields["item_ID"];
if (item_id >=1000) { ... }
----
|outFields | Fields | java.util.Map <String,Object> |
The outgoing task fields. This is implemented as a standard initially empty Java (modifiable) Map.
To create a new schema-compliant instance of a field object see the utility method `subject.getOutFieldSchemaHelper()` below
2+| 2+<a|
*Example:*
[source,javascript,options="nowrap"]
----
executor.outFields["authorised"] = false;
----
|logger | Logger | org.slf4j.ext.XLogger | A helpful logger
2+| 2+<a|
*Example:*
[source,javascript,options="nowrap"]
----
executor.logger.info("Executing task: "
+executor.subject.id);
----
|TRUE/FALSE | boolean | java.land.Boolean | 2 helpful constants. These are useful to retrieve correct return values for the task logic
2+| 2+<a|
*Example:*
[source,javascript,options="nowrap"]
----
var returnValue = executor.TRUE;
// functionally equivalent to:
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 :
[options="compact"]
- *_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
2+| 2+<a|
*Example:*
[source,javascript,options="nowrap"]
----
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");
----
3+l|ContextAlbum getContextAlbum(
String ctxtAlbumName ) |
A utility method to retrieve a `ContextAlbum` for use in the task. This is how you access the context used by the task. The returned `ContextAlbum` implements the `java.util.Map <String,Object>` interface to get and set context as appropriate. The returned `ContextAlbum` also has methods to lock context albums, get information about the schema of the items to be stored in a context album, and get a `SchemaHelper` to manipulate context album items. How to define and use context in a task is described in the Apex Programmer's Guide and in the My First Apex Policy guide.
2+| 2+<a|
*Example:*
[source,javascript,options="nowrap"]
----
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);
----
|====================
|