99157410d5c101196e6b946e9248a449461d9829
[so.git] / bpmn / MSOCoreBPMN / src / main / java / org / onap / so / bpmn / core / BaseTask.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.so.bpmn.core;
22
23 import org.camunda.bpm.engine.delegate.DelegateExecution;
24 import org.camunda.bpm.engine.delegate.Expression;
25 import org.camunda.bpm.engine.delegate.JavaDelegate;
26 import org.onap.so.bpmn.core.internal.VariableNameExtractor;
27
28 /**
29  * Base class for service tasks.
30  */
31 public class BaseTask implements JavaDelegate {
32
33     /**
34      * Get the value of a required field.  This method throws
35      * MissingInjectedFieldException if the expression is null, and
36      * BadInjectedFieldException if the expression evaluates to a null
37      * value.
38      *
39      * @param expression the expression
40      * @param execution the execution
41      * @param fieldName the field name (for logging and exceptions)
42      * @return the field value
43      */
44     protected Object getField(Expression expression,
45             DelegateExecution execution, String fieldName) {
46         return getFieldImpl(expression, execution, fieldName, false);
47     }
48
49     /**
50      * Gets the value of an optional field.  There are three conditions
51      * in which this method returns null:
52      * <p>
53      * <ol>
54      * <li> The expression itself is null (i.e. the field is missing
55      * altogether.</li>
56      * <li>The expression evaluates to a null value.</li>
57      * <li>The expression references a single variable which has not
58      * been set.</li>
59      * </ol>
60      * <p>
61      * Examples:<br>
62      * Expression ${x} when x is null: return null<br>
63      * Expression ${x} when x is unset: return null<br>
64      * Expression ${x+y} when x and/or y are unset: exception<br>
65      *
66      * @param expression the expression
67      * @param execution the execution
68      * @param fieldName the field name (for logging and exceptions)
69      * @return the field value, possibly null
70      */
71     protected Object getOptionalField(Expression expression,
72             DelegateExecution execution, String fieldName) {
73         return getFieldImpl(expression, execution, fieldName, true);
74     }
75
76     /**
77      * Get the value of a required output variable field. This method
78      * throws MissingInjectedFieldException if the expression is null, and
79      * BadInjectedFieldException if the expression produces a null or
80      * illegal variable name.  Legal variable names contain only letters,
81      * numbers, and the underscore character ('_').
82      *
83      * @param expression the expression
84      * @param execution the execution
85      * @param fieldName the field name (for logging and exceptions)
86      * @return the output variable name
87      */
88     protected String getOutputField(Expression expression,
89             DelegateExecution execution, String fieldName) {
90         Object o = getFieldImpl(expression, execution, fieldName, false);
91         if (o instanceof String) {
92             String variable = (String) o;
93             if (!isLegalVariable(variable)) {
94                 throw new BadInjectedFieldException(
95                         fieldName, getTaskName(), "'" + variable
96                         + "' is not a legal variable name");
97             }
98             return variable;
99         } else {
100             throw new BadInjectedFieldException(
101                     fieldName, getTaskName(), "expected a variable name string"
102                     + ", got object of type " + o.getClass().getName());
103         }
104     }
105
106     /**
107      * Get the value of an optional output variable field. This method
108      * throws BadInjectedFieldException if the expression produces an illegal
109      * variable name.  Legal variable names contain only letters, numbers,
110      * and the underscore character ('_').
111      *
112      * @param expression the expression
113      * @param execution the execution
114      * @param fieldName the field name (for logging and exceptions)
115      * @return the output variable name, possibly null
116      */
117     protected String getOptionalOutputField(Expression expression,
118             DelegateExecution execution, String fieldName) {
119         Object o = getFieldImpl(expression, execution, fieldName, true);
120         if (o instanceof String) {
121             String variable = (String) o;
122             if (!isLegalVariable(variable)) {
123                 throw new BadInjectedFieldException(
124                         fieldName, getTaskName(), "'" + variable
125                         + "' is not a legal variable name");
126             }
127             return variable;
128         } else if (o == null) {
129             return null;
130         } else {
131             throw new BadInjectedFieldException(
132                     fieldName, getTaskName(), "expected a variable name string"
133                     + ", got object of type " + o.getClass().getName());
134         }
135     }
136
137     /**
138      * Get the value of a required string field.  This method throws
139      * MissingInjectedFieldException if the expression is null, and
140      * BadInjectedFieldException if the expression evaluates to a null
141      * value.
142      * <p>
143      * Note: the result is coerced to a string value, if necessary.
144      *
145      * @param expression the expression
146      * @param execution the execution
147      * @param fieldName the field name (for logging and exceptions)
148      * @return the field value
149      */
150     protected String getStringField(Expression expression,
151             DelegateExecution execution, String fieldName) {
152         Object o = getFieldImpl(expression, execution, fieldName, false);
153         if (o instanceof String) {
154             return (String) o;
155         } else {
156             throw new BadInjectedFieldException(
157                     fieldName, getTaskName(), "cannot convert '" + o.toString()
158                     + "' to Integer");
159         }
160     }
161
162     /**
163      * Gets the value of an optional string field.  There are three conditions
164      * in which this method returns null:
165      * <p>
166      * <ol>
167      * <li> The expression itself is null (i.e. the field is missing
168      * altogether.</li>
169      * <li>The expression evaluates to a null value.</li>
170      * <li>The expression references a single variable which has not
171      * been set.</li>
172      * </ol>
173      * <p>
174      * Examples:<br>
175      * Expression ${x} when x is null: return null<br>
176      * Expression ${x} when x is unset: return null<br>
177      * Expression ${x+y} when x and/or y are unset: exception<br>
178      * <p>
179      * Note: the result is coerced to a string value, if necessary.
180      *
181      * @param expression the expression
182      * @param execution the execution
183      * @param fieldName the field name (for logging and exceptions)
184      * @return the field value, possibly null
185      */
186     protected String getOptionalStringField(Expression expression,
187             DelegateExecution execution, String fieldName) {
188         Object o = getFieldImpl(expression, execution, fieldName, true);
189         if (o instanceof String) {
190             return (String) o;
191         } else if (o == null) {
192             return null;
193         } else {
194             return o.toString();
195         }
196     }
197
198     /**
199      * Get the value of a required integer field. This method throws
200      * MissingInjectedFieldException if the expression is null, and
201      * BadInjectedFieldException if the expression evaluates to a null
202      * value or a value that cannot be coerced to an integer.
203      *
204      * @param expression the expression
205      * @param execution the execution
206      * @param fieldName the field name (for logging and exceptions)
207      * @return the field value
208      */
209     protected Integer getIntegerField(Expression expression,
210             DelegateExecution execution, String fieldName) {
211         Object o = getFieldImpl(expression, execution, fieldName, false);
212         if (o instanceof Integer) {
213             return (Integer) o;
214         } else {
215             try {
216                 return Integer.parseInt(o.toString());
217             } catch (NumberFormatException e) {
218                 throw new BadInjectedFieldException(
219                         fieldName, getTaskName(), "cannot convert '" + o.toString()
220                         + "' to Integer");
221             }
222         }
223     }
224
225     /**
226      * Gets the value of an optional integer field.  There are three conditions
227      * in which this method returns null:
228      * <p>
229      * <ol>
230      * <li> The expression itself is null (i.e. the field is missing
231      * altogether.</li>
232      * <li>The expression evaluates to a null value.</li>
233      * <li>The expression references a single variable which has not
234      * been set.</li>
235      * </ol>
236      * <p>
237      * Examples:<br>
238      * Expression ${x} when x is null: return null<br>
239      * Expression ${x} when x is unset: return null<br>
240      * Expression ${x+y} when x and/or y are unset: exception<br>
241      * <p>
242      * Note: the result is coerced to an integer value, if necessary. This
243      * method throws BadInjectedFieldException if the result cannot be coerced
244      * to an integer.
245      *
246      * @param expression the expression
247      * @param execution the execution
248      * @param fieldName the field name (for logging and exceptions)
249      * @return the field value, possibly null
250      */
251     protected Integer getOptionalIntegerField(Expression expression,
252             DelegateExecution execution, String fieldName) {
253         Object o = getFieldImpl(expression, execution, fieldName, true);
254         if (o instanceof Integer) {
255             return (Integer) o;
256         } else if (o == null) {
257             return null;
258         } else {
259             try {
260                 return Integer.parseInt(o.toString());
261             } catch (NumberFormatException e) {
262                 throw new BadInjectedFieldException(
263                         fieldName, getTaskName(), "cannot convert '" + o.toString()
264                         + "' to Integer");
265             }
266         }
267     }
268
269     /**
270      * Gets the value of an optional long field.  There are three conditions
271      * in which this method returns null:
272      * <p>
273      * <ol>
274      * <li> The expression itself is null (i.e. the field is missing
275      * altogether.</li>
276      * <li>The expression evaluates to a null value.</li>
277      * <li>The expression references a single variable which has not
278      * been set.</li>
279      * </ol>
280      * <p>
281      * Examples:<br>
282      * Expression ${x} when x is null: return null<br>
283      * Expression ${x} when x is unset: return null<br>
284      * Expression ${x+y} when x and/or y are unset: exception<br>
285      * <p>
286      * Note: the result is coerced to a long value, if necessary. This
287      * method throws BadInjectedFieldException if the result cannot be coerced
288      * to a long.
289      *
290      * @param expression the expression
291      * @param execution the execution
292      * @param fieldName the field name (for logging and exceptions)
293      * @return the field value, possibly null
294      */
295     protected Long getOptionalLongField(Expression expression,
296             DelegateExecution execution, String fieldName) {
297         Object o = getFieldImpl(expression, execution, fieldName, true);
298         if (o instanceof Long) {
299             return (Long) o;
300         } else if (o == null) {
301             return null;
302         } else {
303             try {
304                 return Long.parseLong(o.toString());
305             } catch (NumberFormatException e) {
306                 throw new BadInjectedFieldException(
307                         fieldName, getTaskName(), "cannot convert '" + o.toString()
308                         + "' to Long");
309             }
310         }
311     }
312
313     /**
314      * Get the value of a required long field. This method throws
315      * MissingInjectedFieldException if the expression is null, and
316      * BadInjectedFieldException if the expression evaluates to a null
317      * value or a value that cannot be coerced to a long.
318      *
319      * @param expression the expression
320      * @param execution the execution
321      * @param fieldName the field name (for logging and exceptions)
322      * @return the field value
323      */
324     protected Long getLongField(Expression expression,
325             DelegateExecution execution, String fieldName) {
326         Object o = getFieldImpl(expression, execution, fieldName, false);
327         if (o instanceof Long) {
328             return (Long) o;
329         } else {
330             try {
331                 return Long.parseLong(o.toString());
332             } catch (NumberFormatException e) {
333                 throw new BadInjectedFieldException(
334                         fieldName, getTaskName(), "cannot convert '" + o.toString()
335                         + "' to Long");
336             }
337         }
338     }
339
340     /**
341      * Common implementation for field "getter" methods.
342      *
343      * @param expression the expression
344      * @param execution the execution
345      * @param fieldName the field name (for logging and exceptions)
346      * @param optional true if the field is optional
347      * @return the field value, possibly null
348      */
349     private Object getFieldImpl(Expression expression,
350             DelegateExecution execution, String fieldName, boolean optional) {
351         if (expression == null) {
352             if (!optional) {
353                 throw new MissingInjectedFieldException(
354                         fieldName, getTaskName());
355             }
356             return null;
357         }
358
359         Object value = null;
360
361         try {
362             value = expression.getValue(execution);
363         } catch (Exception e) {
364             if (!optional) {
365                 throw new BadInjectedFieldException(
366                         fieldName, getTaskName(), e.getClass().getSimpleName(), e);
367             }
368
369             // At this point, we have an exception that occurred while
370             // evaluating an expression for an optional field. A common
371             // problem is that the expression is a simple reference to a
372             // variable which has never been set, e.g. the expression is
373             // ${x}. The normal activiti behavior is to throw an exception,
374             // but we don't like that, so we have the following workaround,
375             // which parses the expression text to see if it is a "simple"
376             // variable reference, and if so, returns null.  If the
377             // expression is anything other than a single variable
378             // reference, then an exception is thrown, as it would have
379             // been without this workaround.
380
381             // Get the expression text so we can parse it
382             String s = expression.getExpressionText();
383             new VariableNameExtractor(s).extract().ifPresent(name -> {
384                 if (execution.hasVariable(name)) {
385                     throw new BadInjectedFieldException(
386                             fieldName, getTaskName(), e.getClass().getSimpleName(), e);
387                 }
388             });
389         }
390
391         if (value == null && !optional) {
392             throw new BadInjectedFieldException(
393                     fieldName, getTaskName(), "required field has null value");
394         }
395
396         return value;
397     }
398
399     /**
400      * Tests if a character is a "word" character.
401      *
402      * @param c the character
403      * @return true if the character is a "word" character.
404      */
405     private static boolean isWordCharacter(char c) {
406         return (Character.isLetterOrDigit(c) || c == '_');
407     }
408
409     /**
410      * Tests if the specified string is a legal flow variable name.
411      *
412      * @param name the string
413      * @return true if the string is a legal flow variable name
414      */
415     private boolean isLegalVariable(String name) {
416         if (name == null) {
417             return false;
418         }
419
420         int len = name.length();
421
422         if (len == 0) {
423             return false;
424         }
425
426         char c = name.charAt(0);
427
428         if (!Character.isLetter(c) && c != '_') {
429             return false;
430         }
431
432         for (int i = 1; i < len; i++) {
433             c = name.charAt(i);
434             if (!Character.isLetterOrDigit(c) && c != '_') {
435                 return false;
436             }
437         }
438
439         return true;
440     }
441
442     /**
443      * Returns the name of the task (normally the java class name).
444      *
445      * @return the name of the task
446      */
447     public String getTaskName() {
448         return getClass().getSimpleName();
449     }
450
451     @Override
452     public void execute(DelegateExecution execution) throws Exception {
453     }
454 }