5b43c3fc0799721e3b14ba7344a677843b7fdbf3
[so.git] / bpmn / MSOCoreBPMN / src / main / java / org / openecomp / mso / bpmn / core / BaseTask.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * OPENECOMP - MSO
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.openecomp.mso.bpmn.core;
22
23 import java.util.Objects;
24
25 import org.camunda.bpm.engine.ProcessEngineException;
26 import org.camunda.bpm.engine.delegate.DelegateExecution;
27 import org.camunda.bpm.engine.delegate.Expression;
28 import org.camunda.bpm.engine.delegate.JavaDelegate;
29
30 /**
31  * Base class for service tasks.
32  */
33 public abstract class BaseTask implements JavaDelegate {
34
35         /**
36          * Get the value of a required field.  This method throws
37          * MissingInjectedFieldException if the expression is null, and
38          * BadInjectedFieldException if the expression evaluates to a null
39          * value.
40          *
41          * @param expression the expression
42          * @param execution the execution
43          * @param fieldName the field name (for logging and exceptions)
44          * @return the field value
45          */
46         protected Object getField(Expression expression,
47                         DelegateExecution execution, String fieldName) {
48                 return getFieldImpl(expression, execution, fieldName, false);
49         }
50         
51         /**
52          * Gets the value of an optional field.  There are three conditions
53          * in which this method returns null:
54          * <p>
55          * <ol>
56          * <li> The expression itself is null (i.e. the field is missing
57          * altogether.</li>
58          * <li>The expression evaluates to a null value.</li>
59          * <li>The expression references a single variable which has not
60          * been set.</li>
61          * </ol>
62          * <p>
63          * Examples:<br>
64          * Expression ${x} when x is null: return null<br>
65          * Expression ${x} when x is unset: return null<br>
66          * Expression ${x+y} when x and/or y are unset: exception<br>
67          * 
68          * @param expression the expression
69          * @param execution the execution
70          * @param fieldName the field name (for logging and exceptions)
71          * @return the field value, possibly null
72          */
73         protected Object getOptionalField(Expression expression,
74                         DelegateExecution execution, String fieldName) {
75                 return getFieldImpl(expression, execution, fieldName, true);
76         }
77         
78         /**
79          * Get the value of a required output variable field. This method
80          * throws MissingInjectedFieldException if the expression is null, and
81          * BadInjectedFieldException if the expression produces a null or
82          * illegal variable name.  Legal variable names contain only letters,
83          * numbers, and the underscore character ('_').
84          *
85          * @param expression the expression
86          * @param execution the execution
87          * @param fieldName the field name (for logging and exceptions)
88          * @return the output variable name
89          */
90         protected String getOutputField(Expression expression,
91                         DelegateExecution execution, String fieldName) {
92                 Object o = getFieldImpl(expression, execution, fieldName, false);
93                 if (o instanceof String) {
94                         String variable = (String) o;
95                         if (!isLegalVariable(variable)) {
96                                 throw new BadInjectedFieldException(
97                                         fieldName, getTaskName(), "'" + variable
98                                                 + "' is not a legal variable name");
99                         }
100                         return variable;
101                 } else {
102                         throw new BadInjectedFieldException(
103                                 fieldName, getTaskName(), "expected a variable name string"
104                                         + ", got object of type " + o.getClass().getName());
105                 }
106         }
107         
108         /**
109          * Get the value of an optional output variable field. This method
110          * throws BadInjectedFieldException if the expression produces an illegal
111          * variable name.  Legal variable names contain only letters, numbers,
112          * and the underscore character ('_').
113          * 
114          * @param expression the expression
115          * @param execution the execution
116          * @param fieldName the field name (for logging and exceptions)
117          * @return the output variable name, possibly null
118          */
119         protected String getOptionalOutputField(Expression expression,
120                         DelegateExecution execution, String fieldName) {
121                 Object o = getFieldImpl(expression, execution, fieldName, true);
122                 if (o instanceof String) {
123                         String variable = (String) o;
124                         if (!isLegalVariable(variable)) {
125                                 throw new BadInjectedFieldException(
126                                         fieldName, getTaskName(), "'" + variable
127                                                 + "' is not a legal variable name");
128                         }
129                         return variable;
130                 } else if (o == null) {
131                         return null;
132                 } else {
133                         throw new BadInjectedFieldException(
134                                 fieldName, getTaskName(), "expected a variable name string"
135                                         + ", got object of type " + o.getClass().getName());
136                 }
137         }
138
139         /**
140          * Get the value of a required string field.  This method throws
141          * MissingInjectedFieldException if the expression is null, and
142          * BadInjectedFieldException if the expression evaluates to a null
143          * value.
144          * <p>
145          * Note: the result is coerced to a string value, if necessary.
146          *
147          * @param expression the expression
148          * @param execution the execution
149          * @param fieldName the field name (for logging and exceptions)
150          * @return the field value
151          */
152         protected String getStringField(Expression expression,
153                         DelegateExecution execution, String fieldName) {
154                 Object o = getFieldImpl(expression, execution, fieldName, false);
155                 if (o instanceof String) {
156                         return (String) o;
157                 } else {
158                         throw new BadInjectedFieldException(
159                                 fieldName, getTaskName(), "cannot convert '" + o.toString()
160                                 + "' to Integer");
161                 }
162         }
163         
164         /**
165          * Gets the value of an optional string field.  There are three conditions
166          * in which this method returns null:
167          * <p>
168          * <ol>
169          * <li> The expression itself is null (i.e. the field is missing
170          * altogether.</li>
171          * <li>The expression evaluates to a null value.</li>
172          * <li>The expression references a single variable which has not
173          * been set.</li>
174          * </ol>
175          * <p>
176          * Examples:<br>
177          * Expression ${x} when x is null: return null<br>
178          * Expression ${x} when x is unset: return null<br>
179          * Expression ${x+y} when x and/or y are unset: exception<br>
180          * <p>
181          * Note: the result is coerced to a string value, if necessary.
182          * 
183          * @param expression the expression
184          * @param execution the execution
185          * @param fieldName the field name (for logging and exceptions)
186          * @return the field value, possibly null
187          */
188         protected String getOptionalStringField(Expression expression,
189                         DelegateExecution execution, String fieldName) {
190                 Object o = getFieldImpl(expression, execution, fieldName, true);
191                 return Objects.toString(o, null);
192         }
193         
194         /**
195          * Get the value of a required integer field. This method throws
196          * MissingInjectedFieldException if the expression is null, and
197          * BadInjectedFieldException if the expression evaluates to a null
198          * value or a value that cannot be coerced to an integer.
199          *
200          * @param expression the expression
201          * @param execution the execution
202          * @param fieldName the field name (for logging and exceptions)
203          * @return the field value
204          */
205         protected Integer getIntegerField(Expression expression,
206                         DelegateExecution execution, String fieldName) {
207                 Object o = getFieldImpl(expression, execution, fieldName, false);
208                 if (o instanceof Integer) {
209                         return (Integer) o;
210                 } else {
211                         try {
212                                 return Integer.parseInt(o.toString());
213                         } catch (NumberFormatException e) {
214                                 throw new BadInjectedFieldException(
215                                         fieldName, getTaskName(), "cannot convert '" + o.toString()
216                                         + "' to Integer");
217                         }
218                 }
219         }
220         
221         /**
222          * Gets the value of an optional integer field.  There are three conditions
223          * in which this method returns null:
224          * <p>
225          * <ol>
226          * <li> The expression itself is null (i.e. the field is missing
227          * altogether.</li>
228          * <li>The expression evaluates to a null value.</li>
229          * <li>The expression references a single variable which has not
230          * been set.</li>
231          * </ol>
232          * <p>
233          * Examples:<br>
234          * Expression ${x} when x is null: return null<br>
235          * Expression ${x} when x is unset: return null<br>
236          * Expression ${x+y} when x and/or y are unset: exception<br>
237          * <p>
238          * Note: the result is coerced to an integer value, if necessary. This
239          * method throws BadInjectedFieldException if the result cannot be coerced
240          * to an integer.
241          * 
242          * @param expression the expression
243          * @param execution the execution
244          * @param fieldName the field name (for logging and exceptions)
245          * @return the field value, possibly null
246          */
247         protected Integer getOptionalIntegerField(Expression expression,
248                         DelegateExecution execution, String fieldName) {
249                 Object o = getFieldImpl(expression, execution, fieldName, true);
250                 if (o instanceof Integer) {
251                         return (Integer) o;
252                 } else if (o == null) {
253                         return null;
254                 } else {
255                         try {
256                                 return Integer.parseInt(o.toString());
257                         } catch (NumberFormatException e) {
258                                 throw new BadInjectedFieldException(
259                                         fieldName, getTaskName(), "cannot convert '" + o.toString()
260                                         + "' to Integer");
261                         }
262                 }
263         }
264         
265         /**
266          * Gets the value of an optional long field.  There are three conditions
267          * in which this method returns null:
268          * <p>
269          * <ol>
270          * <li> The expression itself is null (i.e. the field is missing
271          * altogether.</li>
272          * <li>The expression evaluates to a null value.</li>
273          * <li>The expression references a single variable which has not
274          * been set.</li>
275          * </ol>
276          * <p>
277          * Examples:<br>
278          * Expression ${x} when x is null: return null<br>
279          * Expression ${x} when x is unset: return null<br>
280          * Expression ${x+y} when x and/or y are unset: exception<br>
281          * <p>
282          * Note: the result is coerced to a long value, if necessary. This
283          * method throws BadInjectedFieldException if the result cannot be coerced
284          * to a long.
285          * 
286          * @param expression the expression
287          * @param execution the execution
288          * @param fieldName the field name (for logging and exceptions)
289          * @return the field value, possibly null
290          */
291         protected Long getOptionalLongField(Expression expression,
292                         DelegateExecution execution, String fieldName) {
293                 Object o = getFieldImpl(expression, execution, fieldName, true);
294                 if (o instanceof Long) {
295                         return (Long) o;
296                 } else if (o == null) {
297                         return null;
298                 } else {
299                         try {
300                                 return Long.parseLong(o.toString());
301                         } catch (NumberFormatException e) {
302                                 throw new BadInjectedFieldException(
303                                         fieldName, getTaskName(), "cannot convert '" + o.toString()
304                                         + "' to Long");
305                         }
306                 }
307         }
308         
309         /**
310          * Get the value of a required long field. This method throws
311          * MissingInjectedFieldException if the expression is null, and
312          * BadInjectedFieldException if the expression evaluates to a null
313          * value or a value that cannot be coerced to a long.
314          *
315          * @param expression the expression
316          * @param execution the execution
317          * @param fieldName the field name (for logging and exceptions)
318          * @return the field value
319          */
320         protected Long getLongField(Expression expression,
321                         DelegateExecution execution, String fieldName) {
322                 Object o = getFieldImpl(expression, execution, fieldName, false);
323                 if (o instanceof Long) {
324                         return (Long) o;
325                 } else {
326                         try {
327                                 return Long.parseLong(o.toString());
328                         } catch (NumberFormatException e) {
329                                 throw new BadInjectedFieldException(
330                                         fieldName, getTaskName(), "cannot convert '" + o.toString()
331                                         + "' to Long");
332                         }
333                 }
334         }
335
336         /**
337          * Common implementation for field "getter" methods.
338          * @param expression the expression
339          * @param execution the execution
340          * @param fieldName the field name (for logging and exceptions)
341          * @param optional true if the field is optional
342          * @return the field value, possibly null
343          */
344         private Object getFieldImpl(Expression expression,
345                         DelegateExecution execution, String fieldName, boolean optional) {
346                 if (expression == null) {
347                         if (!optional) {
348                                 throw new MissingInjectedFieldException(
349                                         fieldName, getTaskName());
350                         }
351                         return null;
352                 }
353
354                 Object value;
355
356                 try {
357                         value = expression.getValue(execution);
358                 } catch (Exception e) {
359                         if (!optional) {
360                                 throw new BadInjectedFieldException(
361                                         fieldName, getTaskName(), e.getClass().getSimpleName(), e);
362                         }
363
364                         // At this point, we have an exception that occurred while
365                         // evaluating an expression for an optional field. A common 
366                         // problem is that the expression is a simple reference to a
367                         // variable which has never been set, e.g. the expression is
368                         // ${x}. The normal activiti behavior is to throw an exception,
369                         // but we don't like that, so we have the following workaround,
370                         // which parses the expression text to see if it is a "simple"
371                         // variable reference, and if so, returns null.  If the
372                         // expression is anything other than a single variable
373                         // reference, then an exception is thrown, as it would have
374                         // been without this workaround.
375
376                         // Get the expression text so we can parse it
377                         String s = expression.getExpressionText();
378
379 //                      if (isDebugEnabled(execution)) {
380 //                              logDebug(execution, getTaskName() + " field '" + fieldName
381 //                                      + "' expression evaluation failed: " + s);
382 //                      }
383
384                         int len = s.length();
385                         int i = 0;
386
387                         // Skip whitespace
388                         while (i < len && Character.isWhitespace(s.charAt(i))) {
389                                 i++;
390                         }
391
392                         // Next character must be '$'
393                         if (i == len || s.charAt(i++) != '$') {
394                                 throw new BadInjectedFieldException(
395                                         fieldName, getTaskName(), e.getClass().getSimpleName(), e);
396                         }
397
398                         // Skip whitespace
399                         while (i < len && Character.isWhitespace(s.charAt(i))) {
400                                 i++;
401                         }
402
403                         // Next character must be '{'
404                         if (i == len || s.charAt(i++) != '{') {
405                                 throw new BadInjectedFieldException(
406                                         fieldName, getTaskName(), e.getClass().getSimpleName(), e);
407                         }
408
409                         // Skip whitespace
410                         while (i < len && Character.isWhitespace(s.charAt(i))) {
411                                 i++;
412                         }
413
414                         // Collect the variable name
415                         StringBuilder variable = new StringBuilder();
416                         while (i < len && isWordCharacter(s.charAt(i))) {
417                                 variable.append(s.charAt(i));
418                                 i++;
419                         }
420
421                         if (variable.length() == 0) {
422                                 throw new BadInjectedFieldException(
423                                         fieldName, getTaskName(), e.getClass().getSimpleName(), e);
424                         }
425
426                         // Skip whitespace
427                         while (i < len && Character.isWhitespace(s.charAt(i))) {
428                                 i++;
429                         }
430
431                         // Next character must be '}'
432                         if (i == len || s.charAt(i++) != '}') {
433                                 throw new BadInjectedFieldException(
434                                         fieldName, getTaskName(), e.getClass().getSimpleName(), e);
435                         }
436
437                         // Skip whitespace
438                         while (i < len && Character.isWhitespace(s.charAt(i))) {
439                                 i++;
440                         }
441
442                         // Must be at end of string
443                         if (i != len) {
444                                 throw new BadInjectedFieldException(
445                                         fieldName, getTaskName(), e.getClass().getSimpleName(), e);
446                         }
447
448 //                      if (isDebugEnabled(execution)) {
449 //                              logDebug(execution, "Checking if variable '"
450 //                                      + variable.toString() + "' exists");
451 //                      }
452
453                         // If the variable exists then the problem was
454                         // something else...
455                         if (execution.hasVariable(variable.toString())) {
456                                 throw new BadInjectedFieldException(
457                                         fieldName, getTaskName(), e.getClass().getSimpleName(), e);
458                         }
459
460                         // The variable doesn't exist.
461
462 //                      if (isDebugEnabled(execution)) {
463 //                              logDebug(execution, "Variable '" + variable.toString()
464 //                                      + "' does not exist [ok]");
465 //                      }
466
467                         value = null;
468                 }
469
470                 if (value == null && !optional) {
471                         throw new BadInjectedFieldException(
472                                 fieldName, getTaskName(), "required field has null value");
473                 }
474         
475                 return value;
476         }
477         
478         /**
479          * Tests if a character is a "word" character.
480          * @param c the character
481          * @return true if the character is a "word" character.
482          */
483         private boolean isWordCharacter(char c) {
484                 return (Character.isLetterOrDigit(c) || c == '_');
485         }
486         
487         /**
488          * Tests if the specified string is a legal flow variable name.
489          * @param name the string
490          * @return true if the string is a legal flow variable name
491          */
492         private boolean isLegalVariable(String name) {
493                 if (name == null) {
494                         return false;
495                 }
496
497                 int len = name.length();
498
499                 if (len == 0) {
500                         return false;
501                 }
502
503                 char c = name.charAt(0);
504
505                 if (!Character.isLetter(c) && c != '_') {
506                         return false;
507                 }
508
509                 for (int i = 1; i < len; i++) {
510                         c = name.charAt(i);
511                         if (!Character.isLetterOrDigit(c) && c != '_') {
512                                 return false;
513                         }
514                 }
515
516                 return true;
517         }
518         
519         /**
520          * Returns the name of the task (normally the java class name).
521          * @return the name of the task
522          */
523         public String getTaskName() {
524                 return getClass().getSimpleName();
525         }
526
527
528         /**
529          * Check if shouldFail variable is set to true.
530          * @param execution
531          * @return
532          */
533     protected boolean shouldFail(DelegateExecution execution) {
534         Boolean shouldFail = (Boolean) execution.getVariable("shouldFail");
535         return shouldFail != null && shouldFail;
536     }
537 }