ramverma | af74a62 | 2018-07-31 18:25:39 +0100 | [diff] [blame] | 1 | // |
| 2 | // ============LICENSE_START======================================================= |
| 3 | // Copyright (C) 2016-2018 Ericsson. All rights reserved. |
| 4 | // ================================================================================ |
| 5 | // This file is licensed under the CREATIVE COMMONS ATTRIBUTION 4.0 INTERNATIONAL LICENSE |
| 6 | // Full license text at https://creativecommons.org/licenses/by/4.0/legalcode |
| 7 | // |
| 8 | // SPDX-License-Identifier: CC-BY-4.0 |
| 9 | // ============LICENSE_END========================================================= |
| 10 | // |
| 11 | // @author Sven van der Meer (sven.van.der.meer@ericsson.com) |
| 12 | // |
| 13 | |
| 14 | == Writing APEX Task Logic |
| 15 | |
| 16 | Task logic specifies the behavior of an Apex Task. |
| 17 | This logic can be specified in a number of ways, exploiting Apex's plug-in architecture to support a range of logic executors. |
| 18 | In Apex scripted Task Logic can be written in any of these languages: |
| 19 | |
| 20 | * https://en.wikipedia.org/wiki/MVEL[`MVEL`], |
| 21 | * https://en.wikipedia.org/wiki/JavaScript[`JavaScript`], |
| 22 | * https://en.wikipedia.org/wiki/JRuby[`JRuby`] or |
| 23 | * https://en.wikipedia.org/wiki/Jython[`Jython`]. |
| 24 | |
| 25 | These languages were chosen because the scripts can be compiled into Java bytecode at runtime and then efficiently executed natively in the JVM. |
| 26 | Task Logic an also be written directly in Java but needs to be compiled, with the resulting classes added to the classpath. |
| 27 | There are also a number of other Task Logic types (e.g. Fuzzy Logic), but these are not supported as yet. |
| 28 | This guide will focus on the scripted Task Logic approaches, with MVEL and JavaScript being our favorite languages. |
| 29 | In particular this guide will focus on the Apex aspects of the scripts. |
| 30 | However, this guide does not attempt to teach you about the scripting languages themselves ... that is up to you! |
| 31 | |
| 32 | [TIP] |
| 33 | .JVM-based scripting languages |
| 34 | ==== |
| 35 | 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 |
| 36 | ==== |
| 37 | |
| 38 | [NOTE] |
| 39 | .What do Tasks do? |
| 40 | ==== |
| 41 | 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. |
| 42 | 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_. |
| 43 | The state that uses the task is responsible for extracting the _incoming fields_ from the state input event. |
| 44 | 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. |
| 45 | ==== |
| 46 | |
| 47 | First lets start with a sample task, drawn from the "My First Apex Policy" example: |
| 48 | The task "MorningBoozeCheck" from the "My First Apex Policy" example is available in both MVEL and JavaScript: |
| 49 | |
| 50 | .Javascript code for the `MorningBoozeCheck` task |
| 51 | [source,javascript,options="nowrap"] |
| 52 | ---- |
| 53 | include::{adsite-examples-myfirstpolicy-dir}/main/resources/examples/models/MyFirstPolicy/1/MorningBoozeCheck.js[] |
| 54 | ---- |
| 55 | |
| 56 | .MVEL code for the `MorningBoozeCheck` task |
| 57 | [source,java,options="nowrap"] |
| 58 | ---- |
| 59 | include::{adsite-examples-myfirstpolicy-dir}/main/resources/examples/models/MyFirstPolicy/1/MorningBoozeCheck.mvel[] |
| 60 | ---- |
| 61 | |
| 62 | 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`). |
| 63 | |
| 64 | Both MVEL and JavaScript like most JVM-based scripting languages can use standard Java libraries to perform complex tasks. |
| 65 | Towards the top of the scripts you will see how to import Java classes and packages to be used directly in the logic. |
| 66 | Another thing to notice is that Task Logic should return a `java.lang.Boolean` value `true` if the logic executed correctly. |
| 67 | 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. |
| 68 | |
| 69 | [NOTE] |
| 70 | .How to return a value from task logic |
| 71 | ==== |
| 72 | 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). |
| 73 | 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). |
| 74 | |
| 75 | 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). |
| 76 | ==== |
| 77 | |
| 78 | Besides these imported classes and normal language features Apex provides some natively available parameters and functions that can be used directly. |
| 79 | 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. |
| 80 | (These can be accessed using the `executor` keyword for most languages, or can be accessed directly without the `executor` keyword in MVEL): |
| 81 | |
| 82 | .The `executor` Fields / Methods |
| 83 | [width="100%",cols="10l,10d,30m,40a",options="header"] |
| 84 | |==================== |
| 85 | |Name | Type | Java type | Description |
| 86 | |
| 87 | |inFields | Fields | java.util.Map <String,Object> | |
| 88 | The incoming task fields. This is implemented as a standard Java (unmodifiable) Map. |
| 89 | |
| 90 | 2+| 2+<a| |
| 91 | *Example:* |
| 92 | [source,javascript,options="nowrap"] |
| 93 | ---- |
| 94 | executor.logger.debug("Incoming fields: " |
| 95 | +executor.inFields.entrySet()); |
| 96 | var item_id = executor.incomingFields["item_ID"]; |
| 97 | if (item_id >=1000) { ... } |
| 98 | ---- |
| 99 | |
| 100 | |outFields | Fields | java.util.Map <String,Object> | |
| 101 | The outgoing task fields. This is implemented as a standard initially empty Java (modifiable) Map. |
| 102 | To create a new schema-compliant instance of a field object see the utility method `subject.getOutFieldSchemaHelper()` below |
| 103 | |
| 104 | 2+| 2+<a| |
| 105 | *Example:* |
| 106 | [source,javascript,options="nowrap"] |
| 107 | ---- |
| 108 | executor.outFields["authorised"] = false; |
| 109 | ---- |
| 110 | |
| 111 | |logger | Logger | org.slf4j.ext.XLogger | A helpful logger |
| 112 | |
| 113 | 2+| 2+<a| |
| 114 | *Example:* |
| 115 | [source,javascript,options="nowrap"] |
| 116 | ---- |
| 117 | executor.logger.info("Executing task: " |
| 118 | +executor.subject.id); |
| 119 | ---- |
| 120 | |
| 121 | |TRUE/FALSE | boolean | java.land.Boolean | 2 helpful constants. These are useful to retrieve correct return values for the task logic |
| 122 | |
| 123 | 2+| 2+<a| |
| 124 | *Example:* |
| 125 | [source,javascript,options="nowrap"] |
| 126 | ---- |
| 127 | var returnValue = executor.TRUE; |
| 128 | // functionally equivalent to: |
| 129 | var returnValueType = Java.type("java.lang.Boolean"); |
| 130 | var returnValue = new returnValueType(true); |
| 131 | ---- |
| 132 | |
| 133 | |subject | Task | TaskFacade | |
| 134 | |
| 135 | This provides some useful information about the task that contains this task logic. |
| 136 | This object has some useful fields and methods : |
| 137 | |
| 138 | [options="compact"] |
| 139 | - *_AxTask task_* to get access to the full task definition of the host task |
| 140 | - *_String getTaskName()_* to get the name of the host task |
| 141 | - *_String getId()_* to get the ID of the host task |
| 142 | - *_SchemaHelper getInFieldSchemaHelper( String fieldName )_* to get a `SchemaHelper` helper object to manipulate incoming task fields in a schema-aware manner |
| 143 | - *_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 |
| 144 | |
| 145 | 2+| 2+<a| |
| 146 | *Example:* |
| 147 | [source,javascript,options="nowrap"] |
| 148 | ---- |
| 149 | executor.logger.info("Task name: " |
| 150 | +executor.subject.getTaskName()); |
| 151 | executor.logger.info("Task id: " |
| 152 | +executor.subject.getId()); |
| 153 | executor.logger.info("Task inputs definitions: " |
| 154 | +"executor.subject.task.getInputFieldSet()); |
| 155 | executor.logger.info("Task outputs definitions: " |
| 156 | +"executor.subject.task.getOutputFieldSet()); |
| 157 | executor.outFields["authorised"] = executor.subject |
| 158 | .getOutFieldSchemaHelper("authorised") |
| 159 | .createNewInstance("false"); |
| 160 | ---- |
| 161 | |
| 162 | 3+l|ContextAlbum getContextAlbum( |
| 163 | String ctxtAlbumName ) | |
| 164 | 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. |
| 165 | |
| 166 | 2+| 2+<a| |
| 167 | *Example:* |
| 168 | [source,javascript,options="nowrap"] |
| 169 | ---- |
| 170 | var bkey = executor.inFields.get("branch_ID"); |
| 171 | var cnts = executor.getContextMap("BranchCounts"); |
| 172 | cnts.lockForWriting(bkey); |
| 173 | cnts.put(bkey, cnts.get(bkey) + 1); |
| 174 | cnts.unlockForWriting(bkey); |
| 175 | ---- |
| 176 | |==================== |
| 177 | |