2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Modifications Copyright (c) 2019 Samsung
8 * ================================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 * ============LICENSE_END=========================================================
23 package org.onap.so.bpmn.common.scripts;
25 import org.camunda.bpm.engine.delegate.BpmnError
26 import org.camunda.bpm.engine.delegate.DelegateExecution
27 import org.camunda.bpm.engine.variable.VariableMap
28 import org.camunda.bpm.engine.variable.Variables
29 import org.camunda.bpm.engine.variable.Variables.SerializationDataFormats
30 import org.camunda.bpm.engine.variable.impl.value.ObjectValueImpl
31 import org.onap.so.bpmn.common.workflow.context.WorkflowCallbackResponse
32 import org.onap.so.bpmn.common.workflow.context.WorkflowContextHolder
33 import org.onap.so.bpmn.core.UrnPropertiesReader
34 import org.onap.so.bpmn.core.WorkflowException
35 import org.onap.so.client.aai.AAIResourcesClient
36 import org.springframework.web.util.UriUtils
37 import org.slf4j.Logger
38 import org.slf4j.LoggerFactory
40 import groovy.json.JsonSlurper
42 public abstract class AbstractServiceTaskProcessor implements ServiceTaskProcessor {
43 private static final Logger logger = LoggerFactory.getLogger( MsoUtils.class);
45 public MsoUtils utils = new MsoUtils()
48 * Logs a WorkflowException at the ERROR level with the specified message.
49 * @param execution the execution
51 public void logWorkflowException(DelegateExecution execution, String message) {
52 def workflowException = execution.getVariable("WorkflowException")
54 if (workflowException == null) {
55 logger.error(message);
57 logger.error('{}: {}', message, workflowException)
62 * Saves the WorkflowException in the execution to the specified variable,
63 * clearing the WorkflowException variable so the workflow can continue
64 * processing (perhaps catching another WorkflowException).
65 * @param execution the execution
66 * @return the name of the destination variable
68 public saveWorkflowException(DelegateExecution execution, String variable) {
69 if (variable == null) {
70 throw new NullPointerException();
73 execution.setVariable(variable, execution.getVariable("WorkflowException"))
74 execution.setVariable("WorkflowException", null)
79 * Validates that the request exists and that the mso-request-id variable is set.
80 * Additional required variables may be checked by specifying their names.
81 * NOTE: services requiring mso-service-instance-id must specify it explicitly!
82 * If a problem is found, buildAndThrowWorkflowException builds a WorkflowException
83 * and throws an MSOWorkflowException. This method also sets up the log context for
86 * @param execution the execution
87 * @return the validated request
89 public String validateRequest(DelegateExecution execution, String... requiredVariables) {
90 ExceptionUtil exceptionUtil = new ExceptionUtil()
91 def method = getClass().getSimpleName() + '.validateRequest(' +
92 'execution=' + execution.getId() +
93 ', requredVariables=' + requiredVariables +
95 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
96 logger.debug('Entered ' + method)
98 String processKey = getProcessKey(execution)
99 def prefix = execution.getVariable("prefix")
101 if (prefix == null) {
102 exceptionUtil.buildAndThrowWorkflowException(execution, 1002, processKey + " prefix is null")
106 def request = execution.getVariable(prefix + 'Request')
108 if (request == null) {
109 request = execution.getVariable(processKey + 'Request')
111 if (request == null) {
112 request = execution.getVariable('bpmnRequest')
115 setVariable(execution, processKey + 'Request', null)
116 setVariable(execution, 'bpmnRequest', null)
117 setVariable(execution, prefix + 'Request', request)
120 if (request == null) {
121 exceptionUtil.buildAndThrowWorkflowException(execution, 1002, processKey + " request is null")
124 // All requests must have a request ID.
125 // Some requests (e.g. SDN-MOBILITY) do not have a service instance ID.
127 String requestId = null
128 String serviceInstanceId = null
130 List<String> allRequiredVariables = new ArrayList<String>()
131 allRequiredVariables.add("mso-request-id")
133 if (requiredVariables != null) {
134 for (String variable : requiredVariables) {
135 if (!allRequiredVariables.contains(variable)) {
136 allRequiredVariables.add(variable)
141 for (String variable : allRequiredVariables) {
142 def value = execution.getVariable(variable)
143 if (value == null || ((value instanceof CharSequence) && value.length() == 0)) {
144 exceptionUtil.buildAndThrowWorkflowException(execution, 1002, processKey +
145 " request was received with no '" + variable + "' variable")
148 if ("mso-request-id".equals(variable)) {
149 requestId = (String) value
150 } else if ("mso-service-instance-id".equals(variable)) {
151 serviceInstanceId = (String) value
155 if (serviceInstanceId == null) {
156 serviceInstanceId = (String) execution.getVariable("mso-service-instance-id")
159 logger.debug('Incoming message: ' + System.lineSeparator() + request)
160 logger.debug('Exited ' + method)
162 } catch (BpmnError e) {
164 } catch (Exception e) {
165 logger.error('Caught exception in {}: {}', method, e.getMessage(), e)
166 exceptionUtil.buildAndThrowWorkflowException(execution, 1002, "Invalid Message")
171 * gets vars stored in a JSON object in prefix+Request and returns as a LazyMap
172 * setting log context here too
173 * @param execution the execution
174 * @return the inputVars
176 public Map validateJSONReq(DelegateExecution execution) {
177 def method = getClass().getSimpleName() + '.validateJSONReq(' +
178 'execution=' + execution.getId() +
180 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
181 logger.debug('Entered ' + method)
183 String processKey = getProcessKey(execution);
184 def prefix = execution.getVariable("prefix")
186 def request = getVariable(execution, prefix + 'Request')
188 if (request == null) {
189 request = getVariable(execution, processKey + 'Request')
191 if (request == null) {
192 request = getVariable(execution, 'bpmnRequest')
194 execution.setVariable(prefix + 'Request', request)
197 def jsonSlurper = new JsonSlurper()
198 def parsed = jsonSlurper.parseText(request)
201 logger.debug('Incoming message: ' + System.lineSeparator() + request)
202 logger.debug('Exited ' + method)
208 * Sends a response to the workflow service that invoked the process. This method
209 * may only be used by top-level processes that were directly invoked by the
210 * asynchronous workflow service.
211 * @param execution the execution
212 * @param responseCode the response code
213 * @param content the message content
214 * @throws IllegalArgumentException if the response code is invalid
216 * @throws UnsupportedOperationException if not invoked by an asynchronous,
218 * @throws IllegalStateException if a response has already been sent
220 protected void sendWorkflowResponse(DelegateExecution execution, Object responseCode, String response) {
221 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
223 String processKey = getProcessKey(execution);
225 // isAsyncProcess is injected by the workflow service that started the flow
226 if (!String.valueOf(execution.getVariable("isAsyncProcess")).equals("true")) {
227 throw new UnsupportedOperationException(processKey + ": " +
228 "sendWorkflowResponse is valid only in asynchronous workflows");
231 if (String.valueOf(execution.getVariable(processKey + "WorkflowResponseSent")).equals("true")) {
232 logger.debug("Sync response has already been sent for " + processKey)
235 logger.debug("Building " + processKey + " response ")
240 intResponseCode = Integer.parseInt(String.valueOf(responseCode));
242 if (intResponseCode < 100 || intResponseCode > 599) {
243 throw new NumberFormatException(String.valueOf(responseCode));
245 } catch (NumberFormatException e) {
246 throw new IllegalArgumentException("Process " + processKey
247 + " provided an invalid HTTP response code: " + responseCode);
250 // Only 2XX responses are considered "Success"
251 String status = (intResponseCode >= 200 && intResponseCode <= 299) ?
254 // TODO: Should deprecate use of processKey+Response variable for the response. Will use "WorkflowResponse" instead
255 execution.setVariable(processKey + "ResponseCode", String.valueOf(intResponseCode))
256 execution.setVariable(processKey + "Response", response);
257 execution.setVariable(processKey + "Status", status);
258 execution.setVariable("WorkflowResponse", response)
260 logger.debug("Sending response for " + processKey
261 + " ResponseCode=" + intResponseCode
262 + " Status=" + status
263 + " Response=\n" + response)
265 // TODO: ensure that this flow was invoked asynchronously?
267 WorkflowCallbackResponse callbackResponse = new WorkflowCallbackResponse()
268 callbackResponse.setStatusCode(intResponseCode)
269 callbackResponse.setMessage(status)
270 callbackResponse.setResponse(response)
272 // TODO: send this data with HTTP POST
274 WorkflowContextHolder.getInstance().processCallback(
276 execution.getProcessInstanceId(),
277 execution.getVariable("mso-request-id"),
280 execution.setVariable(processKey + "WorkflowResponseSent", "true");
283 } catch (Exception ex) {
284 logger.error("Unable to send workflow response to client ....", ex)
289 * Returns true if a workflow response has already been sent.
290 * @param execution the execution
292 protected boolean isWorkflowResponseSent(DelegateExecution execution) {
293 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
294 String processKey = getProcessKey(execution);
295 return String.valueOf(execution.getVariable(processKey + "WorkflowResponseSent")).equals("true");
299 * Returns the process definition key (i.e. the process name) of the
302 * @param execution the execution
304 public String getProcessKey(DelegateExecution execution) {
305 def testKey = execution.getVariable("testProcessKey")
309 return execution.getProcessEngineServices().getRepositoryService()
310 .getProcessDefinition(execution.getProcessDefinitionId()).getKey()
314 * Returns the process definition key (i.e. the process name) of the
316 * @param execution the execution
318 public String getMainProcessKey(DelegateExecution execution) {
319 DelegateExecution exec = execution
322 DelegateExecution parent = exec.getSuperExecution()
324 if (parent == null) {
325 parent = exec.getParent()
327 if (parent == null) {
335 return execution.getProcessEngineServices().getRepositoryService()
336 .getProcessDefinition(exec.getProcessDefinitionId()).getKey()
340 * Gets the node for the named element from the given xml. If the element
341 * does not exist in the xml or is empty, a WorkflowException is created
342 * (and as a result, a MSOWorkflowException event is thrown).
344 * @param execution The flow's execution.
345 * @param xml Xml to search.
346 * @param elementName Name of element to search for.
347 * @return The element node, if found in the xml.
349 protected String getRequiredNodeXml(DelegateExecution execution, String xml, String elementName) {
350 ExceptionUtil exceptionUtil = new ExceptionUtil()
351 def element = utils.getNodeXml(xml, elementName, false)
352 if (element.trim().isEmpty()) {
353 def msg = 'Required element \'' + elementName + '\' is missing or empty'
355 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
362 * Gets the value of the named element from the given xml. If the element
363 * does not exist in the xml or is empty, a WorkflowException is created
364 * (and as a result, a MSOWorkflowException event is thrown).
366 * @param execution The flow's execution.
367 * @param xml Xml to search.
368 * @param elementName Name of element to whose value to get.
369 * @return The non-empty value of the element, if found in the xml.
371 protected String getRequiredNodeText(DelegateExecution execution, String xml, String elementName) {
372 ExceptionUtil exceptionUtil = new ExceptionUtil()
373 def elementText = utils.getNodeText(xml, elementName)
374 if ((elementText == null) || (elementText.isEmpty())) {
375 def msg = 'Required element \'' + elementName + '\' is missing or empty'
377 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
384 * Get the text for the specified element from the specified xml. If
385 * the element does not exist, return the specified default value.
387 * @param xml Xml from which to get the element's text
388 * @param elementName Name of element whose text to get
389 * @param defaultValue the default value
390 * @return the element's text or the default value if the element does not
391 * exist in the given xml
393 protected String getNodeText(String xml, String elementName, String defaultValue) {
394 def nodeText = utils.getNodeText(xml, elementName)
395 return (nodeText == null) ? defaultValue : nodeText
399 * Get the text for the specified element from the specified xml. If
400 * the element does not exist, return an empty string.
402 * @param xml Xml from which to get the element's text.
403 * @param elementName Name of element whose text to get.
404 * @return the element's text or an empty string if the element does not
405 * exist in the given xml.
407 protected String getNodeTextForce(String xml, String elementName) {
408 return getNodeText(xml, elementName, '');
412 *Store the variable as typed with java serialization type
417 public void setVariable(DelegateExecution execution, String name, Object value) {
418 VariableMap variables = Variables.createVariables()
419 variables.putValueTyped('payload', Variables.objectValue(value)
420 .serializationDataFormat(SerializationDataFormats.JAVA) // tells the engine to use java serialization for persisting the value
422 execution.setVariable(name,variables)
425 //TODO not sure how this will look in Cockpit
428 * Returns the variable map
433 public static String getVariable(DelegateExecution execution, String name) {
434 def myObj = execution.getVariable(name)
435 if(myObj instanceof VariableMap){
436 VariableMap serializedObjectMap = (VariableMap) myObj
437 ObjectValueImpl payloadObj = serializedObjectMap.getValueTyped('payload')
438 return payloadObj.getValue()
446 * Returns true if a value equals one of the provided set. Equality is
447 * determined by using the equals method if the value object and the
448 * object in the provided set have the same class. Otherwise, the objects
449 * are converted to strings and then compared. Nulls are permitted for
450 * the value as well as in the provided set
453 * def statusCode = getStatusCode()
454 * isOneOf(statusCode, 200, 201, 204)
456 * @param value the value to test
457 * @param these a set of permissable values
458 * @return true if the value is in the provided set
460 public boolean isOneOf(Object value, Object... these) {
461 for (Object thisOne : these) {
462 if (thisOne == null) {
468 if (value.getClass() == thisOne.getClass()) {
469 if (value.equals(thisOne)) {
473 if (String.valueOf(value).equals(String.valueOf(thisOne))) {
484 * Sets flows success indicator variable.
487 public void setSuccessIndicator(DelegateExecution execution, boolean isSuccess) {
488 String prefix = execution.getVariable('prefix')
489 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
491 logger.debug('Entered SetSuccessIndicator Method')
492 execution.setVariable(prefix+'SuccessIndicator', isSuccess)
493 logger.debug('Outgoing SuccessIndicator is: ' + execution.getVariable(prefix+'SuccessIndicator') + '')
497 * Sends a Error Sync Response
500 public void sendSyncError(DelegateExecution execution) {
501 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
502 String requestId = execution.getVariable("mso-request-id")
503 logger.debug('sendSyncError, requestId: ' + requestId)
504 WorkflowException workflowExceptionObj = execution.getVariable("WorkflowException")
505 if (workflowExceptionObj != null) {
506 String errorMessage = workflowExceptionObj.getErrorMessage()
507 def errorCode = workflowExceptionObj.getErrorCode()
508 logger.debug('sendSyncError, requestId: ' + requestId + ' | errorMessage: ' + errorMessage + ' | errorCode: ' + errorCode)
509 sendWorkflowResponse(execution, errorCode, errorMessage)
514 * Executes a named groovy script method in the current object
516 public void executeMethod(String methodName, Object... args) {
518 if (args != null && args.size() > 0) {
520 // First argument of method to call is always the execution object
521 DelegateExecution execution = (DelegateExecution) args[0]
523 def classAndMethod = getClass().getSimpleName() + '.' + methodName + '(execution=' + execution.getId() + ')'
524 def isDebugLogEnabled = execution.getVariable('isDebugLogEnabled')
526 logger.debug('Entered ' + classAndMethod)
527 logger.debug('Received parameters: ' + args)
530 def methodToCall = this.metaClass.getMetaMethod(methodName, args)
531 logger.debug('Method to call: ' + methodToCall)
532 methodToCall?.invoke(this, args)
534 catch(BpmnError bpmnError) {
535 logger.debug('Rethrowing BpmnError ' + bpmnError.getMessage())
539 logger.debug('Unexpected error encountered - {}', e.getMessage(), e)
540 (new ExceptionUtil()).buildAndThrowWorkflowException(execution, 9999, e.getMessage())
543 logger.debug('Exited ' + classAndMethod)
549 *This method determines and adds the appropriate ending to come
550 *after a number (-st, -nd, -rd, or -th)
554 *@return String ending - number with suffix
556 public static String labelMaker(Object n) {
558 if(n instanceof String){
559 num = Integer.parseInt(n)
564 String ending = ""; //the end to be added to the number
566 if ((num % 10 == 1) && (num != 11)) {
568 } else if ((num % 10 == 2) && (num != 12)) {
570 } else if ((num % 10 == 3) && (num != 13)) {
580 * Constructs a workflow message callback URL for the specified message type and correlator.
581 * This type of callback URL is used when a workflow wants an MSO adapter (like the SDNC
582 * adapter) to call it back. In other words, this is for callbacks internal to the MSO
583 * complex. Use <code>createWorkflowMessageAdapterCallbackURL</code> if the callback
584 * will come from outside the MSO complex.
585 * @param messageType the message type (e.g. SDNCAResponse or VNFAResponse)
586 * @param correlator the correlator value (e.g. a request ID)
588 public String createCallbackURL(DelegateExecution execution, String messageType, String correlator) {
589 String endpoint = UrnPropertiesReader.getVariable("mso.workflow.message.endpoint", execution)
591 if (endpoint == null || endpoint.isEmpty()) {
592 ExceptionUtil exceptionUtil = new ExceptionUtil()
593 exceptionUtil.buildAndThrowWorkflowException(execution, 2000,
594 'mso:workflow:message:endpoint URN mapping is not set')
597 while (endpoint.endsWith('/')) {
598 endpoint = endpoint.substring(0, endpoint.length()-1)
602 '/' + UriUtils.encodePathSegment(messageType, 'UTF-8') +
603 '/' + UriUtils.encodePathSegment(correlator, 'UTF-8')
608 * Constructs a workflow message callback URL for the specified message type and correlator.
609 * This type of callback URL is used when a workflow wants a system outside the MSO complex
610 * to call it back through the Workflow Message Adapter.
611 * @param messageType the message type (e.g. SNIROResponse)
612 * @param correlator the correlator value (e.g. a request ID)
614 public String createWorkflowMessageAdapterCallbackURL(DelegateExecution execution, String messageType, String correlator) {
615 String endpoint = UrnPropertiesReader.getVariable("mso.adapters.workflow.message.endpoint", execution)
617 if (endpoint == null || endpoint.isEmpty()) {
618 ExceptionUtil exceptionUtil = new ExceptionUtil()
619 exceptionUtil.buildAndThrowWorkflowException(execution, 2000,
620 'mso:adapters:workflow:message:endpoint URN mapping is not set')
623 while (endpoint.endsWith('/')) {
624 endpoint = endpoint.substring(0, endpoint.length()-1)
628 '/' + UriUtils.encodePathSegment(messageType, 'UTF-8') +
629 '/' + UriUtils.encodePathSegment(correlator, 'UTF-8')
632 public void setRollbackEnabled(DelegateExecution execution, isDebugLogEnabled) {
635 def prefix = execution.getVariable('prefix')
636 def disableRollback = execution.getVariable("disableRollback")
637 def defaultRollback = UrnPropertiesReader.getVariable("mso.rollback", execution).toBoolean()
639 logger.debug('disableRollback: ' + disableRollback)
640 logger.debug('defaultRollback: ' + defaultRollback)
644 if(disableRollback == null || disableRollback == '' ) {
645 // get from default urn settings for mso_rollback
646 disableRollback = !defaultRollback
647 rollbackEnabled = defaultRollback
648 logger.debug('disableRollback is null or empty!')
651 if(disableRollback == true) {
652 rollbackEnabled = false
654 else if(disableRollback == false){
655 rollbackEnabled = true
658 rollbackEnabled = defaultRollback
662 execution.setVariable(prefix+"backoutOnFailure", rollbackEnabled)
663 logger.debug('rollbackEnabled (aka backoutOnFailure): ' + rollbackEnabled)
666 public void setBasicDBAuthHeader(DelegateExecution execution, isDebugLogEnabled) {
668 String basicAuthValueDB = UrnPropertiesReader.getVariable("mso.adapters.db.auth", execution)
669 def encodedString = utils.getBasicAuth(basicAuthValueDB, UrnPropertiesReader.getVariable("mso.msoKey", execution))
670 execution.setVariable("BasicAuthHeaderValueDB",encodedString)
671 } catch (IOException ex) {
672 String dataErrorMessage = " Unable to encode Catalog DB user/password string - " + ex.getMessage()
673 logger.debug(dataErrorMessage)
674 (new ExceptionUtil()).buildAndThrowWorkflowException(execution, 2500, dataErrorMessage)
677 public AAIResourcesClient getAAIClient(){
678 return new AAIResourcesClient();