Initial OpenECOMP MSO commit
[so.git] / bpmn / MSOGammaBPMN / src / main / groovy / com / att / bpm / scripts / AbstractServiceTaskProcessor.groovy
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 com.att.bpm.scripts;
22
23 import javax.xml.transform.Transformer
24 import javax.xml.transform.TransformerException;
25
26 import org.camunda.bpm.engine.delegate.BpmnError
27 import org.camunda.bpm.engine.impl.core.variable.value.ObjectValueImpl
28 import org.camunda.bpm.engine.runtime.Execution
29 import org.camunda.bpm.engine.variable.VariableMap
30
31
32
33 import org.openecomp.mso.bpmn.core.WorkflowException
34 import org.openecomp.mso.bpmn.gamma.workflow.service.WorkflowCallbackResponse
35 import org.openecomp.mso.bpmn.gamma.workflow.service.WorkflowContextHolder
36
37 import groovy.json.JsonSlurper
38
39 import org.openecomp.mso.rest.APIResponse;
40 import org.openecomp.mso.rest.RESTClient
41 import org.openecomp.mso.rest.RESTConfig
42
43 import org.camunda.bpm.engine.variable.VariableMap;
44 import org.camunda.bpm.engine.variable.Variables;
45 import org.camunda.bpm.engine.variable.Variables.SerializationDataFormats;
46 import org.w3c.dom.Node;
47
48 public abstract class AbstractServiceTaskProcessor implements ServiceTaskProcessor {
49         public MsoUtils utils = new MsoUtils()
50
51
52         /**
53          * Logs a message at the ERROR level.
54          * @param message the message
55          */
56         public void logError(String message) {
57                 log('ERROR', message, null, "true")
58         }
59
60         /**
61          * Logs a message at the ERROR level.
62          * @param message the message
63          * @param cause the cause (stracktrace will be included in the output)
64          */
65         public void logError(String message, Throwable cause) {
66                 log('ERROR', message, cause, "true")
67         }
68
69         /**
70          * Logs a message at the WARN level.
71          * @param message the message
72          */
73         public void logWarn(String message) {
74                 log('WARN', message, null, "true")
75         }
76
77         /**
78          * Logs a message at the WARN level.
79          * @param message the message
80          * @param cause the cause (stracktrace will be included in the output)
81          */
82         public void logWarn(String message, Throwable cause) {
83                 log('WARN', message, cause, "true")
84         }
85
86         /**
87          * Logs a message at the INFO level.
88          * @param message the message
89          */
90         public void logInfo(String message) {
91                 log('INFO', message, null, "true")
92         }
93
94         /**
95          * Logs a message at the INFO level.
96          * @param message the message
97          * @param cause the cause (stracktrace will be included in the output)
98          */
99         public void logInfo(String message, Throwable cause) {
100                 log('INFO', message, cause, "true")
101         }
102
103         /**
104          * Logs a message at the DEBUG level.
105          * @param message the message
106          * @param isDebugLogEnabled a flag indicating if DEBUG level is enabled
107          */
108         public void logDebug(String message, String isDebugLogEnabled) {
109                 log('DEBUG', message, null, isDebugLogEnabled)
110         }
111
112         /**
113          * Logs a message at the DEBUG level.
114          * @param message the message
115          * @param cause the cause (stracktrace will be included in the output)
116          * @param isDebugLogEnabled a flag indicating if DEBUG level is enabled
117          */
118         public void logDebug(String message, Throwable cause, String isDebugLogEnabled) {
119                 log('DEBUG', message, cause, isDebugLogEnabled)
120         }
121
122         /**
123          * Logs a message at the specified level.
124          * @param level the level (DEBUG, INFO, WARN, ERROR)
125          * @param message the message
126          * @param isLevelEnabled a flag indicating if the level is enabled
127          *        (used only at the DEBUG level)
128          */
129         public void log(String level, String message, String isLevelEnabled) {
130                 log(level, message,  null, isLevelEnabled)
131         }
132
133         /**
134          * Logs a message at the specified level.
135          * @param level the level (DEBUG, INFO, WARN, ERROR)
136          * @param message the message
137          * @param cause the cause (stracktrace will be included in the output)
138          * @param isLevelEnabled a flag indicating if the level is enabled
139          *        (used only at the DEBUG level)
140          */
141         public void log(String level, String message, Throwable cause, String isLevelEnabled) {
142                 if (cause == null) {
143                         utils.log(level, message, isLevelEnabled);
144                 } else {
145                         StringWriter stringWriter = new StringWriter();
146                         PrintWriter printWriter = new PrintWriter(stringWriter);
147                         printWriter.println(message);
148                         cause.printStackTrace(printWriter);
149                         utils.log(level, stringWriter.toString(), isLevelEnabled);
150                         printWriter.close();
151                 }
152         }
153
154         /**
155          * Logs a WorkflowException at the ERROR level with the specified message.
156          * @param execution the execution
157          */
158         public void logWorkflowException(Execution execution, String message) {
159                 def workflowException = execution.getVariable("WorkflowException")
160
161                 if (workflowException == null) {
162                         logError(message);
163                 } else {
164                         logError(message + ": " + workflowException)
165                 }
166         }
167
168         /**
169          * Saves the WorkflowException in the execution to the specified variable,
170          * clearing the WorkflowException variable so the workflow can continue
171          * processing (perhaps catching another WorkflowException).
172          * @param execution the execution
173          * @return the name of the destination variable
174          */
175         public saveWorkflowException(Execution execution, String variable) {
176                 if (variable == null) {
177                         throw new NullPointerException();
178                 }
179
180                 execution.setVariable(variable, execution.getVariable("WorkflowException"))
181                 execution.setVariable("WorkflowException", null)
182         }
183
184         /**
185          * Builds a success response from the specified message content and numeric
186          * response code.  The response code may be an integer or a string representation
187          * of an integer.  The response is stored in the execution where it may be
188          * picked up by the Workflow service.
189          * <p>
190          * IMPORTANT: the activity that executes this method should have an
191          * asynchronous continuation after it to ensure the execution variables
192          * are persisted to the database.
193          * @param execution the execution
194          * @param content the message content
195          * @param responseCode the message response code
196          */
197         @Deprecated
198         public void buildResponse(Execution execution, String content, Object responseCode) {
199                 buildResponse(execution, content, responseCode, true)
200         }
201
202         /**
203          * Builds a standard error response containing the specified error message and
204          * numeric response code.  The response code may be an integer or a string
205          * representation of an integer.  The response is stored in the execution where
206          * it may be picked up by the Workflow service.
207          * <p>
208          * IMPORTANT: the activity that executes this method should have an
209          * asynchronous continuation after it to ensure the execution variables
210          * are persisted to the database.
211          * @param execution the execution
212          * @param content the message content
213          * @param errorCode the message response code
214          */
215         @Deprecated
216         public void buildErrorResponse(Execution execution, String errorMessage, Object errorCode) {
217
218                 def encErrorMessage = errorMessage.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
219
220                 def content = """
221                         <aetgt:WorkflowException xmlns:aetgt="http://ecomp.att.com/mso/workflow/schema/v1">
222                         <aetgt:ErrorMessage>${encErrorMessage}</aetgt:ErrorMessage>
223                         <aetgt:ErrorCode>${errorCode}</aetgt:ErrorCode>
224                         </aetgt:WorkflowException>
225                   """
226
227                 buildResponse(execution, content, errorCode, false)
228         }
229
230         // BEGIN LEGACY SUPPORT.  TODO: REMOVE THIS CODE
231         /**
232          * Builds a standard error response containing the specified error message
233          * and a numeric response code.  The response code is obtained from the
234          * prefix+"ResponseCode" execution variable. The response is stored in the
235          * execution where it may be picked up by the Workflow service.
236          * <p>
237          * IMPORTANT: the activity that executes this method should have an
238          * asynchronous continuation after it to ensure the execution variables
239          * are persisted to the database.
240          * <p>
241          * This method is deprecated. Methods that accept a response code should
242          * be used instead.
243          * @param execution the execution
244          * @param errorMessage the error message for the error response
245          */
246         @Deprecated
247         public void buildErrorResponse(Execution execution, String errorMessage) {
248                 buildErrorResponse(execution, errorMessage, null)
249         }
250         // END LEGACY SUPPORT.  TODO: REMOVE THIS CODE
251
252         /**
253          * Builds a response from the specified message content and numeric response
254          * code.  The response code may be an integer or a string representation of
255          * an integer.  The response is stored in the execution where it may be
256          * picked up by the Workflow service.
257          * <p>
258          * IMPORTANT: the activity that executes this method should have an
259          * asynchronous continuation after it to ensure the execution variables
260          * are persisted to the database.
261          * @param execution the execution
262          * @param content the message content
263          * @param responseCode the message response code
264          * @param isSuccess true if this is a success response
265          */
266         @Deprecated
267         protected void buildResponse(Execution execution, String content, Object responseCode,
268                         boolean isSuccess) {
269                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
270
271                 String processKey = getProcessKey(execution);
272                 logDebug("Building " + processKey + " response", isDebugLogEnabled)
273
274                 Map<String, Object> responseMap = new HashMap<String, Object>()
275
276                 if (isSuccess) {
277                         responseMap.put("Status", "Success")
278                 } else {
279                         responseMap.put("Status", "Fail")
280                 }
281
282                 // BEGIN LEGACY SUPPORT.  TODO: REMOVE THIS CODE
283                 def prefix = execution.getVariable("prefix")
284
285                 if (responseCode == null) {
286                         responseCode = execution.getVariable(prefix+"ResponseCode")
287                 } else {
288                         execution.setVariable(prefix+"ResponseCode", String.valueOf(responseCode))
289                 }
290                 // END LEGACY SUPPORT.  TODO: REMOVE THIS CODE
291
292                 responseMap.put("ResponseCode", String.valueOf(responseCode))
293
294                 if (isSuccess) {
295                         responseMap.put("Status", "Success")
296                         // TODO: Should deprecate use of processKey+Response variable for the response. Will use "WorkflowResponse" instead
297                         execution.setVariable("WorkflowResponse", content)
298                         // BEGIN LEGACY SUPPORT.  TODO: REMOVE THIS CODE
299                         execution.setVariable(processKey+"Response", content)
300                         execution.setVariable(prefix+"ErrorResponse", null)
301                         // END LEGACY SUPPORT.  TODO: REMOVE THIS CODE
302                 } else {
303                         responseMap.put("Status", "Fail")
304                         // BEGIN LEGACY SUPPORT.  TODO: REMOVE THIS CODE
305                         execution.setVariable(prefix+"ErrorResponse", content)
306                         execution.setVariable(prefix+"Response", null)
307                         // END LEGACY SUPPORT.  TODO: REMOVE THIS CODE
308                 }
309
310                 responseMap.put("Response", content)
311
312                 logDebug(processKey
313                         + " ResponseCode=" + responseMap.get("ResponseCode")
314                         + " Status=" + responseMap.get("Status")
315                         + " Response=\n" + responseMap.get("Response"),
316                         isDebugLogEnabled)
317
318                 execution.setVariable(processKey + "ResponseMap", responseMap)
319         }
320
321         /**
322          * Builds an error response (if one has not already been built) and throws
323          * a BpmnError of type "MSOWorkflowException" that can be caught as a
324          * boundary event.
325          * @param execution the execution
326          * @param errorMessage the error message for the error response
327          * @param responseCode the message response code
328          */
329         @Deprecated
330         public void workflowException(Execution execution, String errorMessage, Object responseCode) {
331                 String processKey = getProcessKey(execution);
332
333                 buildErrorResponse(execution, errorMessage, responseCode)
334                 throw new BpmnError("MSOWorkflowException")
335         }
336
337         /**
338          * Puts a WorkflowException into the execution
339          * @param execution the execution
340          * @param errorCode the error code (normally a 4-digit number)
341          * @param errorMessage the error message
342          */
343         @Deprecated
344         public void newWorkflowException(Execution execution, int errorCode, String errorMessage) {
345                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
346                 String processKey = getProcessKey(execution);
347                 logDebug("Building a " + processKey + " WorkflowException", isDebugLogEnabled)
348
349                 if (errorCode < 1000) {
350                         throw new IllegalArgumentException("ErrorCode must be a number greater than 1000");
351                 }
352
353                 WorkflowException exception = new WorkflowException(processKey, errorCode, errorMessage);
354                 execution.setVariable("WorkflowException", exception);
355         }
356
357         /**
358          * Puts a WorkflowException into the execution and throws an MSOWorkflowException event.
359          * @param execution the execution
360          * @param errorCode the error code (normally a 4-digit number)
361          * @param errorMessage the error message
362          */
363         // TODO: rename this method to be throwWorkflowException
364         @Deprecated
365         public void createWorkflowException(Execution execution, int errorCode, String errorMessage) {
366                 newWorkflowException(execution, errorCode, errorMessage)
367                 throw new BpmnError("MSOWorkflowException", "errorCode:" + errorCode + ", errorMessage:" + errorMessage)
368         }
369
370         /**
371          * Puts a WorkflowException into the execution and throws an MSOWorkflowException event.
372          * @param execution the execution
373          * @param errorCode the error code (normally a 4-digit number)
374          * @param errorMessage the error message
375          */
376         @Deprecated
377         public void commonWorkflowException(Execution execution, int errorCode, String errorMessage) {
378                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
379                 String processKey = getProcessKey(execution);
380                 logDebug("Building a " + processKey + " WorkflowException", isDebugLogEnabled)
381                 logError(errorMessage)
382                 WorkflowException exception = new WorkflowException(processKey, errorCode, errorMessage);
383                 execution.setVariable("WorkflowException", exception);
384                 throw new BpmnError("MSOWorkflowException","errorCode:" + errorCode + ",errorMessage:" + errorMessage)
385         }
386
387         /**
388          * Puts a WorkflowException into the execution and throws an MSOWorkflowException event.
389          * @param execution the execution
390          * @param errorCode the error code (normally a 4-digit number)
391          * @param errorMessage the error message
392          */
393         @Deprecated
394         public void commonWorkflowException(Execution execution, String errorCode, String errorMessage) {
395                 int intRespCode
396                 try{
397                         intRespCode = Integer.parseInt(errorCode)
398                 }catch (Exception e){
399                         intRespCode = 400
400                 }
401                 commonWorkflowException(execution, intRespCode, errorMessage)
402         }
403
404
405
406         /**
407          * Validates that the request exists and that the att-mso-request-id variable is set.
408          * Additional required variables may be checked by specifying their names.
409          * NOTE: services requiring att-mso-service-instance-id must specify it explicitly!
410          * If a problem is found, buildAndThrowWorkflowException builds a WorkflowException
411          * and throws an MSOWorkflowException.  This method also sets up the log context for
412          * the workflow.
413          *
414          * @param execution the execution
415          * @return the validated request
416          */
417         public String validateRequest(Execution execution, String... requiredVariables) {
418                 ExceptionUtil exceptionUtil = new ExceptionUtil()
419                 def method = getClass().getSimpleName() + '.validateRequest(' +
420                         'execution=' + execution.getId() +
421                         ', requredVariables=' + requiredVariables +
422                         ')'
423                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
424                 logDebug('Entered ' + method, isDebugLogEnabled)
425
426                 String processKey = getProcessKey(execution)
427                 def prefix = execution.getVariable("prefix")
428
429                 if (prefix == null) {
430                         exceptionUtil.buildAndThrowWorkflowException(execution, 1002, processKey + " prefix is null")
431                 }
432
433                 try {
434                         def request = execution.getVariable(prefix + 'Request')
435
436                         if (request == null) {
437                                 request = execution.getVariable(processKey + 'Request')
438
439                                 if (request == null) {
440                                         request = execution.getVariable('bpmnRequest')
441                                 }
442
443                                 setVariable(execution, processKey + 'Request', null)
444                                 setVariable(execution, 'bpmnRequest', null)
445                                 setVariable(execution, prefix + 'Request', request)
446                         }
447
448                         if (request == null) {
449                                 exceptionUtil.buildAndThrowWorkflowException(execution, 1002, processKey + " request is null")
450                         }
451
452                         // All requests must have a request ID.
453                         // Some requests (e.g. SDN-MOBILITY) do not have a service instance ID.
454
455                         String requestId = null
456                         String serviceInstanceId = null
457
458                         List<String> allRequiredVariables = new ArrayList<String>()
459                         allRequiredVariables.add("att-mso-request-id")
460
461                         if (requiredVariables != null) {
462                                 for (String variable : requiredVariables) {
463                                         if (!allRequiredVariables.contains(variable)) {
464                                                 allRequiredVariables.add(variable)
465                                         }
466                                 }
467                         }
468
469                         for (String variable : allRequiredVariables) {
470                                 def value = execution.getVariable(variable)
471                                 if (value == null || ((value instanceof CharSequence) && value.length() == 0)) {
472                                         exceptionUtil.buildAndThrowWorkflowException(execution, 1002, processKey +
473                                                 " request was received with no '" + variable + "' variable")
474                                 }
475
476                                 if ("att-mso-request-id".equals(variable)) {
477                                         requestId = (String) value
478                                 } else if ("att-mso-service-instance-id".equals(variable)) {
479                                         serviceInstanceId = (String) value
480                                 }
481                         }
482
483                         if (serviceInstanceId == null) {
484                                 serviceInstanceId = (String) execution.getVariable("att-mso-service-instance-id")
485                         }
486
487                         utils.logContext(requestId, serviceInstanceId)
488                         logDebug('Incoming message: ' + System.lineSeparator() + request, isDebugLogEnabled)
489                         logDebug('Exited ' + method, isDebugLogEnabled)
490                         return request
491                 } catch (BpmnError e) {
492                         throw e
493                 } catch (Exception e) {
494                         logError('Caught exception in ' + method, e)
495                         exceptionUtil.buildAndThrowWorkflowException(execution, 1002, "Invalid Message")
496                 }
497         }
498
499         /**
500          * gets vars stored in a JSON object in prefix+Request and returns as a LazyMap
501          * setting log context here too
502          * @param execution the execution
503          * @return the inputVars
504          */
505         public Map validateJSONReq(Execution execution) {
506                 def method = getClass().getSimpleName() + '.validateJSONReq(' +
507                                 'execution=' + execution.getId() +
508                                 ')'
509                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
510                 logDebug('Entered ' + method, isDebugLogEnabled)
511
512                 String processKey = getProcessKey(execution);
513                 def prefix = execution.getVariable("prefix")
514
515                 def requestId =getVariable(execution, "att-mso-request-id")
516                 def serviceInstanceId = getVariable(execution, "att-mso-service-instance-id")
517                 if(requestId!=null && serviceInstanceId!=null){
518                         utils.logContext(requestId, serviceInstanceId)
519                 }
520
521
522                 def request = getVariable(execution, prefix + 'Request')
523
524                 if (request == null) {
525                         request = getVariable(execution, processKey + 'Request')
526
527                         if (request == null) {
528                                 request = getVariable(execution, 'bpmnRequest')
529                         }
530                         execution.setVariable(prefix + 'Request', request)
531                 }
532
533                 def jsonSlurper = new JsonSlurper()
534                 def parsed = jsonSlurper.parseText(request)
535
536
537                 logDebug('Incoming message: ' + System.lineSeparator() + request, isDebugLogEnabled)
538                 logDebug('Exited ' + method, isDebugLogEnabled)
539                 return parsed
540
541         }
542
543
544
545
546         /**
547          * Sends a response to the workflow service that invoked the process.  This method
548          * may only be used by top-level processes that were directly invoked by the
549          * asynchronous workflow service.
550          * @param execution the execution
551          * @param responseCode the response code
552          * @param content the message content
553          * @throws IllegalArgumentException if the response code is invalid
554          *         by HTTP standards
555          * @throws UnsupportedOperationException if not invoked by an asynchronous,
556          *         top-level process
557          * @throws IllegalStateException if a response has already been sent
558          */
559         protected void sendWorkflowResponse(Execution execution, Object responseCode, String response) {
560                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
561                 try {
562                         String processKey = getProcessKey(execution);
563
564                         // isAsyncProcess is injected by the workflow service that started the flow
565                         if (!String.valueOf(execution.getVariable("isAsyncProcess")).equals("true")) {
566                                 throw new UnsupportedOperationException(processKey + ": " +
567                                         "sendWorkflowResponse is valid only in asynchronous workflows");
568                         }
569
570                         if (String.valueOf(execution.getVariable(processKey + "WorkflowResponseSent")).equals("true")) {
571                                         logDebug("Sync response has already been sent for " + processKey, isDebugLogEnabled)
572                         }else{
573
574                                 logDebug("Building " + processKey + " response ", isDebugLogEnabled)
575
576                                 int intResponseCode;
577
578                                 try {
579                                         intResponseCode = Integer.parseInt(String.valueOf(responseCode));
580
581                                         if (intResponseCode < 100 || intResponseCode > 599) {
582                                                 throw new NumberFormatException(String.valueOf(responseCode));
583                                         }
584                                 } catch (NumberFormatException e) {
585                                         throw new IllegalArgumentException("Process " + processKey
586                                                 + " provided an invalid HTTP response code: " + responseCode);
587                                 }
588
589                                 // Only 2XX responses are considered "Success"
590                                 String status = (intResponseCode >= 200 && intResponseCode <= 299) ?
591                                         "Success" : "Fail";
592
593                                 // TODO: Should deprecate use of processKey+Response variable for the response. Will use "WorkflowResponse" instead
594                                 execution.setVariable(processKey + "ResponseCode", String.valueOf(intResponseCode))
595                                 execution.setVariable(processKey + "Response", response);
596                                 execution.setVariable(processKey + "Status", status);
597                                 execution.setVariable("WorkflowResponse", response)
598
599                                 logDebug("Sending response for " + processKey
600                                         + " ResponseCode=" + intResponseCode
601                                         + " Status=" + status
602                                         + " Response=\n" + response,
603                                         isDebugLogEnabled)
604
605                                 // TODO: ensure that this flow was invoked asynchronously?
606
607                                 WorkflowCallbackResponse callbackResponse = new WorkflowCallbackResponse()
608                                 callbackResponse.setStatusCode(intResponseCode)
609                                 callbackResponse.setMessage(status)
610                                 callbackResponse.setResponse(response)
611
612                                 // TODO: send this data with HTTP POST
613
614                                 WorkflowContextHolder.getInstance().processCallback(
615                                         processKey,
616                                         execution.getProcessInstanceId(),
617                                         execution.getVariable("att-mso-request-id"),
618                                         callbackResponse)
619
620                                 execution.setVariable(processKey + "WorkflowResponseSent", "true");
621                         }
622
623                 } catch (Exception ex) {
624                         logError("Unable to send workflow response to client ....", ex)
625                 }
626         }
627
628         /**
629          * Returns true if a workflow response has already been sent.
630          * @param execution the execution
631          */
632         protected boolean isWorkflowResponseSent(Execution execution) {
633                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
634                 String processKey = getProcessKey(execution);
635                 return String.valueOf(execution.getVariable(processKey + "WorkflowResponseSent")).equals("true");
636         }
637
638         /**
639          * Returns the process definition key (i.e. the process name) from the
640          * execution.
641          * @param execution the execution
642          */
643         public String getProcessKey(Execution execution) {
644                 def testKey = execution.getVariable("testProcessKey")
645                 if(testKey!=null){
646                         return testKey
647                 }
648                 return execution.getProcessEngineServices().getRepositoryService()
649                         .getProcessDefinition(execution.getProcessDefinitionId()).getKey()
650         }
651
652         /**
653          * Gets the node for the named element from the given xml. If the element
654          * does not exist in the xml or is empty, a WorkflowException is created
655          * (and as a result, a MSOWorkflowException event is thrown).
656          *
657          * @param execution The flow's execution.
658          * @param xml Xml to search.
659          * @param elementName Name of element to search for.
660          * @return The element node, if found in the xml.
661          */
662         protected String getRequiredNodeXml(Execution execution, String xml, String elementName) {
663                 def element = utils.getNodeXml(xml, elementName, false)
664                 if (element.trim().isEmpty()) {
665                         def msg = 'Required element \'' + elementName + '\' is missing or empty'
666                         logError(msg)
667                         createWorkflowException(execution, 2000, msg)
668                 } else {
669                         return element
670                 }
671         }
672
673         /**
674          * Gets the value of the named element from the given xml. If the element
675          * does not exist in the xml or is empty, a WorkflowException is created
676          * (and as a result, a MSOWorkflowException event is thrown).
677          *
678          * @param execution The flow's execution.
679          * @param xml Xml to search.
680          * @param elementName Name of element to whose value to get.
681          * @return The value of the element, if found in the xml.
682          */
683         protected String getRequiredNodeText(Execution execution, String xml, String elementName) {
684                 def elementText = utils.getNodeText1(xml, elementName)
685                 if (elementText == null) {
686                         def msg = 'Required element \'' + elementName + '\' is missing or empty'
687                         logError(msg)
688                         createWorkflowException(execution, 2000, msg)
689                 } else {
690                         return elementText
691                 }
692         }
693
694         /**
695          * Get the text for the specified element from the specified xml.  If
696          * the element does not exist, return an empty string.
697          *
698          * @param xml Xml from which to get the element's text.
699          * @param elementName Name of element whose text to get.
700          * @return the element's text or an empty string if the element does not
701          * exist in the given xml.
702          */
703         protected String getNodeTextForce(String xml, String elementName) {
704                 def nodeText = utils.getNodeText1(xml, elementName)
705                 return (nodeText == null) ? '' : nodeText
706         }
707
708         /**
709          * Sends the empty, synchronous response back to the API Handler.
710          * @param execution the execution
711          */
712         @Deprecated
713         public void sendResponse(Execution execution) {
714                 def method = getClass().getSimpleName() + '.sendResponse(' +
715                         'execution=' + execution.getId() +
716                         ')'
717                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
718                 logDebug('Entered ' + method, isDebugLogEnabled)
719
720                 try {
721                         buildResponse(execution, "", 200)
722                         logDebug('Exited ' + method, isDebugLogEnabled)
723                 } catch (BpmnError e) {
724                         throw e;
725                 } catch (Exception e) {
726                         logError('Caught exception in ' + method, e)
727                         workflowException(execution, 'Internal Error', 9999) // TODO: what message and error code?
728                 }
729         }
730
731         /**
732         *Store the variable as typed with java serialization type
733         *@param execution
734         *@param name
735         *@param value
736         */
737         public void setVariable(Execution execution, String name, Object value) {
738                 VariableMap variables = Variables.createVariables()
739                 variables.putValueTyped('payload', Variables.objectValue(value)
740                 .serializationDataFormat(SerializationDataFormats.JAVA) // tells the engine to use java serialization for persisting the value
741                 .create())
742                 execution.setVariable(name,variables)
743         }
744
745         //TODO not sure how this will look in Cockpit
746
747         /**
748          * Returns the variable map
749         *@param execution
750         *@param name
751         *@return
752         **/
753         public String getVariable(Execution execution, String name) {
754                 def myObj = execution.getVariable(name)
755                 if(myObj instanceof VariableMap){
756                         VariableMap serializedObjectMap = (VariableMap) myObj
757                         ObjectValueImpl payloadObj = serializedObjectMap.getValueTyped('payload')
758                         return payloadObj.getValue()
759                 }else{
760                         return myObj
761                 }
762         }
763
764
765         /**
766          * Returns true if a value equals one of the provided set. Equality is
767          * determined by using the equals method if the value object and the
768          * object in the provided set have the same class. Otherwise, the objects
769          * are converted to strings and then compared.  Nulls are permitted for
770          * the value as well as in the provided set
771          * Example:
772          * <pre>
773          *     def statusCode = getStatusCode()
774          *     isOneOf(statusCode, 200, 201, 204)
775          * </pre>
776          * @param value the value to test
777          * @param these a set of permissable values
778          * @return true if the value is in the provided set
779          */
780         public boolean isOneOf(Object value, Object... these) {
781                 for (Object thisOne : these) {
782                         if (thisOne == null) {
783                                 if (value == null) {
784                                         return true
785                                 }
786                         } else {
787                                 if (value != null) {
788                                         if (value.getClass() == thisOne.getClass()) {
789                                                 if (value.equals(thisOne)) {
790                                                         return true
791                                                 }
792                                         } else {
793                                                 if (String.valueOf(value).equals(String.valueOf(thisOne))) {
794                                                         return true
795                                                 }
796                                         }
797                                 }
798                         }
799                 }
800
801                 return false
802         }
803
804         public void setSuccessIndicator(Execution execution, boolean isSuccess) {
805                 String prefix = execution.getVariable('prefix')
806                 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
807
808                 logDebug('Entered SetSuccessIndicator Method', isDebugLogEnabled)
809                 execution.setVariable(prefix+'SuccessIndicator', isSuccess)
810                 logDebug('Outgoing SuccessIndicator is: ' + execution.getVariable(prefix+'SuccessIndicator') + '', isDebugLogEnabled)
811         }
812
813
814         public void sendSyncError(Execution execution) {
815                 def isDebugEnabled=execution.getVariable("isDebugLogEnabled")
816                 String requestId = execution.getVariable("att-mso-request-id")
817                 logDebug('sendSyncError, requestId: ' + requestId, isDebugEnabled)
818                 WorkflowException workflowExceptionObj = execution.getVariable("WorkflowException")
819                 if (workflowExceptionObj != null) {
820                         String errorMessage = workflowExceptionObj.getErrorMessage()
821                         def errorCode = workflowExceptionObj.getErrorCode()
822                         logDebug('sendSyncError, requestId: '  + requestId + ' | errorMessage: ' + errorMessage + ' | errorCode: ' + errorCode, isDebugEnabled)
823                         sendWorkflowResponse(execution, errorCode, errorMessage)
824                 }
825         }
826         
827         /**
828          * Create a WorkflowException - uses ExceptionUtil to build a WorkflowException
829          * @param execution
830          * @param errorCode
831          * @param errorMessage
832          * @param isDebugEnabled
833          */
834         public void buildWorkflowException(Execution execution, int errorCode, String errorMessage, boolean isDebugEnabled) {
835                 (new ExceptionUtil()).buildWorkflowException(execution, errorCode, errorMessage)
836         }
837         
838         /**
839          * Executes a named groovy script method in the current object
840          */
841         public void executeMethod(String methodName, Object... args) {
842                 
843                 if (args != null && args.size() > 0) {
844                         
845                         // First argument of method to call is always the execution object
846                         Execution execution = (Execution) args[0]
847
848                         def classAndMethod = getClass().getSimpleName() + '.' + methodName + '(execution=' + execution.getId() + ')'
849                         def isDebugEnabled =  execution.getVariable('isDebugLogEnabled')
850
851                         logDebug('Entered ' + classAndMethod, isDebugEnabled)
852                         logDebug('Received parameters: ' + args, isDebugEnabled)
853
854                         try{
855                                 def methodToCall = this.metaClass.getMetaMethod(methodName, args)
856                                 logDebug('Method to call: ' + methodToCall, isDebugEnabled)
857                                 methodToCall?.invoke(this, args)
858                         }
859                         catch(BpmnError bpmnError) {
860                                 logDebug('Rethrowing BpmnError ' + bpmnError.getMessage(), isDebugEnabled)
861                                 throw bpmnError
862                         }
863                         catch(Exception e) {
864                                 e.printStackTrace()
865                                 logDebug('Unexpected error encountered - ' + e.getMessage(), isDebugEnabled)
866                                 (new ExceptionUtil()).buildAndThrowWorkflowException(execution, 9999, e.getMessage())
867                         }
868                         finally {
869                                 logDebug('Exited ' + classAndMethod, isDebugEnabled)
870                         }
871                 }
872         }
873
874 }