Bugfixes for December 2018
[so.git] / bpmn / so-bpmn-tasks / src / main / java / org / onap / so / bpmn / infrastructure / workflow / tasks / WorkflowActionBBTasks.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 - 2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.so.bpmn.infrastructure.workflow.tasks;
22
23 import java.sql.Timestamp;
24 import java.util.ArrayList;
25 import java.util.Date;
26 import java.util.List;
27 import java.util.Optional;
28
29 import org.camunda.bpm.engine.delegate.BpmnError;
30 import org.camunda.bpm.engine.delegate.DelegateExecution;
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.WorkflowException;
34 import org.onap.so.bpmn.servicedecomposition.entities.BuildingBlock;
35 import org.onap.so.bpmn.servicedecomposition.entities.ExecuteBuildingBlock;
36 import org.onap.so.client.exception.ExceptionBuilder;
37 import org.onap.so.db.request.beans.InfraActiveRequests;
38 import org.onap.so.db.request.client.RequestsDbClient;
39 import org.onap.so.serviceinstancebeans.RequestReferences;
40 import org.onap.so.serviceinstancebeans.ServiceInstancesResponse;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43 import org.springframework.beans.factory.annotation.Autowired;
44 import org.springframework.stereotype.Component;
45
46 import com.fasterxml.jackson.core.JsonProcessingException;
47 import com.fasterxml.jackson.databind.ObjectMapper;
48
49 @Component
50 public class WorkflowActionBBTasks {
51
52         private static final String G_CURRENT_SEQUENCE = "gCurrentSequence";
53         private static final String G_REQUEST_ID = "mso-request-id";
54         private static final String G_ALACARTE = "aLaCarte";
55         private static final String G_ACTION = "requestAction";
56         private static final String RETRY_COUNT = "retryCount";
57         private static final Logger logger = LoggerFactory.getLogger(WorkflowActionBBTasks.class);
58
59         @Autowired
60         private RequestsDbClient requestDbclient;
61         @Autowired
62         private WorkflowAction workflowAction;
63         @Autowired
64         private WorkflowActionBBFailure workflowActionBBFailure;
65         
66         public void selectBB(DelegateExecution execution) {
67                 List<ExecuteBuildingBlock> flowsToExecute = (List<ExecuteBuildingBlock>) execution
68                                 .getVariable("flowsToExecute");
69                 execution.setVariable("MacroRollback", false);
70                 int currentSequence = (int) execution.getVariable(G_CURRENT_SEQUENCE);
71                 ExecuteBuildingBlock ebb = flowsToExecute.get(currentSequence);
72                 boolean homing = (boolean) execution.getVariable("homing");
73                 boolean calledHoming = (boolean) execution.getVariable("calledHoming");
74                 if (homing && !calledHoming) {
75                         if (ebb.getBuildingBlock().getBpmnFlowName().equals("AssignVnfBB")) {
76                                 ebb.setHoming(true);
77                                 execution.setVariable("calledHoming", true);
78                         }
79                 } else {
80                         ebb.setHoming(false);
81                 }
82                 execution.setVariable("buildingBlock", ebb);
83                 currentSequence++;
84                 if (currentSequence >= flowsToExecute.size()) {
85                         execution.setVariable("completed", true);
86                 } else {
87                         execution.setVariable("completed", false);
88                         execution.setVariable(G_CURRENT_SEQUENCE, currentSequence);
89                 }
90         }
91         
92         public void updateFlowStatistics(DelegateExecution execution) {
93                 try{
94                         int currentSequence = (int) execution.getVariable(G_CURRENT_SEQUENCE);
95                         if(currentSequence > 1) {
96                                 InfraActiveRequests request = this.getUpdatedRequest(execution, currentSequence);
97                                 requestDbclient.updateInfraActiveRequests(request);
98                         }
99                 }catch (Exception ex){
100                         logger.warn("Bpmn Flow Statistics was unable to update Request Db with the new completion percentage. Competion percentage may be invalid.");
101                 }
102         }
103
104         protected InfraActiveRequests getUpdatedRequest(DelegateExecution execution, int currentSequence) {
105                 List<ExecuteBuildingBlock> flowsToExecute = (List<ExecuteBuildingBlock>) execution
106                                 .getVariable("flowsToExecute");
107                 String requestId = (String) execution.getVariable(G_REQUEST_ID);
108                 InfraActiveRequests request = requestDbclient.getInfraActiveRequestbyRequestId(requestId);
109                 ExecuteBuildingBlock completedBB = flowsToExecute.get(currentSequence - 2);
110                 ExecuteBuildingBlock nextBB = flowsToExecute.get(currentSequence - 1);
111                 int completedBBs = currentSequence - 1;
112                 int totalBBs = flowsToExecute.size();
113                 int remainingBBs = totalBBs - completedBBs;
114                 String statusMessage = this.getStatusMessage(completedBB.getBuildingBlock().getBpmnFlowName(), 
115                                 nextBB.getBuildingBlock().getBpmnFlowName(), completedBBs, remainingBBs);
116                 Long percentProgress = this.getPercentProgress(completedBBs, totalBBs);
117                 request.setFlowStatus(statusMessage);
118                 request.setProgress(percentProgress);
119                 request.setLastModifiedBy("CamundaBPMN");
120                 return request;
121         }
122         
123         protected Long getPercentProgress(int completedBBs, int totalBBs) {
124                 double ratio = (completedBBs / (totalBBs * 1.0));
125                 int percentProgress = (int) (ratio * 95);
126                 return new Long(percentProgress + 5);
127         }
128         
129         protected String getStatusMessage(String completedBB, String nextBB, int completedBBs, int remainingBBs) {
130                 return "Execution of " + completedBB + " has completed successfully, next invoking " + nextBB
131                                 + " (Execution Path progress: BBs completed = " + completedBBs + "; BBs remaining = " + remainingBBs
132                                 + ").";
133         }
134
135         public void sendSyncAck(DelegateExecution execution) {
136                 final String requestId = (String) execution.getVariable(G_REQUEST_ID);
137                 final String resourceId = (String) execution.getVariable("resourceId");
138                 ServiceInstancesResponse serviceInstancesResponse = new ServiceInstancesResponse();
139                 RequestReferences requestRef = new RequestReferences();
140                 requestRef.setInstanceId(resourceId);
141                 requestRef.setRequestId(requestId);
142                 serviceInstancesResponse.setRequestReferences(requestRef);
143                 ObjectMapper mapper = new ObjectMapper();
144                 String jsonRequest = "";
145                 try {
146                         jsonRequest = mapper.writeValueAsString(serviceInstancesResponse);
147                 } catch (JsonProcessingException e) {
148                         workflowAction.buildAndThrowException(execution,
149                                         "Could not marshall ServiceInstancesRequest to Json string to respond to API Handler.", e);
150                 }
151                 WorkflowCallbackResponse callbackResponse = new WorkflowCallbackResponse();
152                 callbackResponse.setStatusCode(200);
153                 callbackResponse.setMessage("Success");
154                 callbackResponse.setResponse(jsonRequest);
155                 String processKey = execution.getProcessEngineServices().getRepositoryService()
156                                 .getProcessDefinition(execution.getProcessDefinitionId()).getKey();
157                 WorkflowContextHolder.getInstance().processCallback(processKey, execution.getProcessInstanceId(), requestId,
158                                 callbackResponse);
159                 logger.info("Successfully sent sync ack.");
160         }
161
162         public void sendErrorSyncAck(DelegateExecution execution) {
163                 final String requestId = (String) execution.getVariable(G_REQUEST_ID);
164                 try {
165                         ExceptionBuilder exceptionBuilder = new ExceptionBuilder();
166                         String errorMsg = (String) execution.getVariable("WorkflowActionErrorMessage");
167                         if (errorMsg == null) {
168                                 errorMsg = "WorkflowAction failed unexpectedly.";
169                         }
170                         String processKey = exceptionBuilder.getProcessKey(execution);
171                         String buildworkflowException = "<aetgt:WorkflowException xmlns:aetgt=\"http://org.onap/so/workflow/schema/v1\"><aetgt:ErrorMessage>"
172                                         + errorMsg
173                                         + "</aetgt:ErrorMessage><aetgt:ErrorCode>7000</aetgt:ErrorCode></aetgt:WorkflowException>";
174                         WorkflowCallbackResponse callbackResponse = new WorkflowCallbackResponse();
175                         callbackResponse.setStatusCode(500);
176                         callbackResponse.setMessage("Fail");
177                         callbackResponse.setResponse(buildworkflowException);
178                         WorkflowContextHolder.getInstance().processCallback(processKey, execution.getProcessInstanceId(), requestId,
179                                         callbackResponse);
180                         execution.setVariable("sentSyncResponse", true);
181                 } catch (Exception ex) {
182                         logger.error(" Sending Sync Error Activity Failed. {}"  , ex.getMessage(), ex);
183                 }
184         }
185
186         public void updateRequestStatusToComplete(DelegateExecution execution) {
187                 try{
188                         final String requestId = (String) execution.getVariable(G_REQUEST_ID);
189                         InfraActiveRequests request = requestDbclient.getInfraActiveRequestbyRequestId(requestId);
190                         final String action = (String) execution.getVariable(G_ACTION);
191                         final boolean aLaCarte = (boolean) execution.getVariable(G_ALACARTE);
192                         final String resourceName = (String) execution.getVariable("resourceName");
193                         String macroAction = "";
194                         if (aLaCarte) {
195                                 macroAction = "ALaCarte-" + resourceName + "-" + action + " request was executed correctly.";
196                         } else {
197                                 macroAction = "Macro-" + resourceName + "-" + action + " request was executed correctly.";
198                         }
199                         execution.setVariable("finalStatusMessage", macroAction);
200                         Timestamp endTime = new Timestamp(System.currentTimeMillis());
201                         request.setEndTime(endTime);
202                         request.setStatusMessage(macroAction);
203                         request.setProgress(Long.valueOf(100));
204                         request.setRequestStatus("COMPLETE");
205                         request.setLastModifiedBy("CamundaBPMN");
206                         requestDbclient.updateInfraActiveRequests(request);
207                 }catch (Exception ex) {
208                         workflowAction.buildAndThrowException(execution, "Error Updating Request Database", ex);
209                 }
210         }
211
212         public void checkRetryStatus(DelegateExecution execution) {
213                 String handlingCode = (String) execution.getVariable("handlingCode");
214                 String requestId = (String) execution.getVariable(G_REQUEST_ID);
215                 String retryDuration = (String) execution.getVariable("RetryDuration");
216                 int retryCount = (int) execution.getVariable(RETRY_COUNT);
217                 int nextCount = retryCount +1;
218                 if (handlingCode.equals("Retry")){
219                         workflowActionBBFailure.updateRequestErrorStatusMessage(execution);
220                         try{
221                                 InfraActiveRequests request = requestDbclient.getInfraActiveRequestbyRequestId(requestId);
222                                 request.setRetryStatusMessage("Retry " + nextCount + "/5 will be started in " + retryDuration);
223                                 requestDbclient.updateInfraActiveRequests(request); 
224                         } catch(Exception ex){
225                                 logger.warn("Failed to update Request Db Infra Active Requests with Retry Status",ex);
226                         }
227                         if(retryCount<5){
228                                 int currSequence = (int) execution.getVariable("gCurrentSequence");
229                                 execution.setVariable("gCurrentSequence", currSequence-1);
230                                 execution.setVariable(RETRY_COUNT, nextCount);
231                         }else{
232                                 workflowAction.buildAndThrowException(execution, "Exceeded maximum retries. Ending flow with status Abort");
233                         }
234                 }else{
235                         execution.setVariable(RETRY_COUNT, 0);
236                 }
237         }
238
239         /**
240          * Rollback will only handle Create/Activate/Assign Macro flows. Execute
241          * layer will rollback the flow its currently working on.
242          */
243         public void rollbackExecutionPath(DelegateExecution execution) {
244                 if(!(boolean)execution.getVariable("isRollback")){
245                         List<ExecuteBuildingBlock> flowsToExecute = (List<ExecuteBuildingBlock>) execution
246                                         .getVariable("flowsToExecute");
247                         List<ExecuteBuildingBlock> rollbackFlows = new ArrayList();
248                         int currentSequence = (int) execution.getVariable(G_CURRENT_SEQUENCE);
249                         int listSize = flowsToExecute.size();
250                         for (int i = listSize - 1; i >= 0; i--) {
251                                 if (i > currentSequence - 1) {
252                                         flowsToExecute.remove(i);
253                                 } else {
254                                         String flowName = flowsToExecute.get(i).getBuildingBlock().getBpmnFlowName();
255                                         if (flowName.contains("Assign")) {
256                                                 flowName = "Unassign" + flowName.substring(6, flowName.length());
257                                         } else if (flowName.contains("Create")) {
258                                                 flowName = "Delete" + flowName.substring(6, flowName.length());
259                                         } else if (flowName.contains("Activate")) {
260                                                 flowName = "Deactivate" + flowName.substring(8, flowName.length());
261                                         }else{
262                                                 continue;
263                                         }
264                                         flowsToExecute.get(i).getBuildingBlock().setBpmnFlowName(flowName);
265                                         rollbackFlows.add(flowsToExecute.get(i));
266                                 }
267                         }
268                         
269                         int flowSize = rollbackFlows.size();
270                         String handlingCode = (String) execution.getVariable("handlingCode");
271                         if(handlingCode.equals("RollbackToAssigned")){
272                                 for(int i = 0; i<flowSize -1; i++){
273                                         if(rollbackFlows.get(i).getBuildingBlock().getBpmnFlowName().contains("Unassign")){
274                                                 rollbackFlows.remove(i);
275                                         }
276                                 }
277                         }
278                         
279                         workflowActionBBFailure.updateRequestErrorStatusMessage(execution);
280                         
281                         if (rollbackFlows.isEmpty())
282                                 execution.setVariable("isRollbackNeeded", false);
283                         else
284                                 execution.setVariable("isRollbackNeeded", true);
285                         execution.setVariable("flowsToExecute", rollbackFlows);
286                         execution.setVariable("handlingCode", "PreformingRollback");
287                         execution.setVariable("isRollback", true);
288                         execution.setVariable("gCurrentSequence", 0);
289                         execution.setVariable(RETRY_COUNT, 0);
290                 }else{
291                         workflowAction.buildAndThrowException(execution, "Rollback has already been called. Cannot rollback a request that is currently in the rollback state.");
292                 }
293         }
294
295         protected void updateRequestErrorStatusMessage(DelegateExecution execution) {
296                 try {
297                         String requestId = (String) execution.getVariable(G_REQUEST_ID);
298                         InfraActiveRequests request = requestDbclient.getInfraActiveRequestbyRequestId(requestId);
299                         String errorMsg = retrieveErrorMessage(execution);
300                         if(errorMsg == null || errorMsg.equals("")){
301                                 errorMsg = "Failed to determine error message";
302                         }
303                         request.setStatusMessage(errorMsg);
304                         logger.debug("Updating RequestDB to failed: errorMsg = " + errorMsg);
305                         requestDbclient.updateInfraActiveRequests(request);
306                 } catch (Exception e) {
307                         logger.error("Failed to update Request db with the status message after retry or rollback has been initialized.",e);
308                 }
309         }
310
311         public void abortCallErrorHandling(DelegateExecution execution) {
312                 String msg = "Flow has failed. Rainy day handler has decided to abort the process.";
313                 logger.error(msg);
314                 throw new BpmnError(msg);
315         }
316         
317         public void updateRequestStatusToFailed(DelegateExecution execution) {
318                 try {
319                         String requestId = (String) execution.getVariable(G_REQUEST_ID);
320                         InfraActiveRequests request = requestDbclient.getInfraActiveRequestbyRequestId(requestId);
321                         String errorMsg = null;
322                         String rollbackErrorMsg = null;
323                         boolean rollbackCompleted = (boolean) execution.getVariable("isRollbackComplete");
324                         boolean isRollbackFailure = (boolean) execution.getVariable("isRollback");
325                         ExecuteBuildingBlock ebb = (ExecuteBuildingBlock) execution.getVariable("buildingBlock");
326                         
327                         if(rollbackCompleted){
328                                 rollbackErrorMsg = "Rollback has been completed successfully.";
329                                 request.setRollbackStatusMessage(rollbackErrorMsg);
330                                 logger.debug("Updating RequestDB to failed: Rollback has been completed successfully");
331                         }else{
332                                 if(isRollbackFailure){
333                                         rollbackErrorMsg = retrieveErrorMessage(execution);
334                                         if(rollbackErrorMsg == null || rollbackErrorMsg.equals("")){
335                                                 rollbackErrorMsg = "Failed to determine rollback error message.";
336                                         }
337                                         request.setRollbackStatusMessage(rollbackErrorMsg);
338                                         logger.debug("Updating RequestDB to failed: rollbackErrorMsg = " + rollbackErrorMsg);
339                                 }else{
340                                         errorMsg = retrieveErrorMessage(execution);
341                                         if(errorMsg == null || errorMsg.equals("")){
342                                                 errorMsg = "Failed to determine error message";
343                                         }
344                                         request.setStatusMessage(errorMsg);
345                                         logger.debug("Updating RequestDB to failed: errorMsg = " + errorMsg);
346                                 }
347                         }
348                         if(ebb!=null && ebb.getBuildingBlock()!=null){
349                                 String flowStatus = ebb.getBuildingBlock().getBpmnFlowName() + " has failed.";
350                                 request.setFlowStatus(flowStatus);
351                                 execution.setVariable("flowStatus", flowStatus);
352                         }
353
354                         request.setProgress(Long.valueOf(100));
355                         request.setRequestStatus("FAILED");
356                         request.setLastModifiedBy("CamundaBPMN");
357                         requestDbclient.updateInfraActiveRequests(request);
358                 } catch (Exception e) {
359                         workflowAction.buildAndThrowException(execution, "Error Updating Request Database", e);
360                 }
361         }
362         
363         private String retrieveErrorMessage (DelegateExecution execution){
364                 String errorMsg = "";
365                 try {
366                         WorkflowException exception = (WorkflowException) execution.getVariable("WorkflowException");
367                         if(exception != null && (exception.getErrorMessage()!=null || !exception.getErrorMessage().equals(""))){
368                                 errorMsg = exception.getErrorMessage();
369                         }
370                 } catch (Exception ex) {
371                         //log error and attempt to extact WorkflowExceptionMessage
372                         logger.error("Failed to extract workflow exception from execution.",ex);
373                 }
374                 
375                 if (errorMsg == null || errorMsg.equals("")){
376                         try {
377                                 errorMsg = (String) execution.getVariable("WorkflowExceptionErrorMessage");
378                         } catch (Exception ex) {
379                                 logger.error("Failed to extract workflow exception message from WorkflowException",ex);
380                                 errorMsg = "Unexpected Error in BPMN.";
381                         }
382                 }
383                 return errorMsg;
384         }
385         
386         public void updateRequestStatusToFailedWithRollback(DelegateExecution execution) {
387                 execution.setVariable("isRollbackComplete", true);
388                 updateRequestStatusToFailed(execution);
389         }
390 }