1 /* Copyright (c) 2019 AT&T Intellectual Property. #
\r
3 # Licensed under the Apache License, Version 2.0 (the "License"); #
\r
4 # you may not use this file except in compliance with the License. #
\r
5 # You may obtain a copy of the License at #
\r
7 # http://www.apache.org/licenses/LICENSE-2.0 #
\r
9 # Unless required by applicable law or agreed to in writing, software #
\r
10 # distributed under the License is distributed on an "AS IS" BASIS, #
\r
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
\r
12 # See the License for the specific language governing permissions and #
\r
13 # limitations under the License. #
\r
14 ##############################################################################*/
\r
17 package org.oran.otf.camunda.delegate.otf.common.runnable;
\r
19 import org.oran.otf.camunda.configuration.OtfCamundaConfiguration;
\r
20 import org.oran.otf.camunda.exception.TestExecutionException;
\r
21 import org.oran.otf.camunda.exception.WorkflowProcessorException;
\r
22 import org.oran.otf.camunda.service.ProcessEngineAwareService;
\r
23 import org.oran.otf.camunda.workflow.WorkflowProcessor;
\r
24 import org.oran.otf.camunda.workflow.WorkflowRequest;
\r
25 import org.oran.otf.common.model.TestExecution;
\r
26 import org.oran.otf.common.repository.TestExecutionRepository;
\r
27 import org.oran.otf.common.utility.database.TestExecutionUtility;
\r
28 import com.mongodb.client.result.UpdateResult;
\r
29 import java.util.Collections;
\r
30 import java.util.HashMap;
\r
31 import java.util.Map;
\r
32 import java.util.Timer;
\r
33 import java.util.TimerTask;
\r
34 import java.util.concurrent.Callable;
\r
35 import org.camunda.bpm.BpmPlatform;
\r
36 import org.camunda.bpm.engine.RuntimeService;
\r
37 import org.camunda.bpm.engine.runtime.ProcessInstance;
\r
38 import org.oran.otf.camunda.model.ExecutionConstants;
\r
39 import org.slf4j.Logger;
\r
40 import org.slf4j.LoggerFactory;
\r
41 import org.springframework.data.mongodb.core.MongoTemplate;
\r
42 import org.springframework.data.mongodb.core.query.Criteria;
\r
43 import org.springframework.data.mongodb.core.query.Query;
\r
44 import org.springframework.data.mongodb.core.query.Update;
\r
46 public class SynchronousTestInstanceCallable extends ProcessEngineAwareService
\r
47 implements Callable<TestExecution> {
\r
49 private static final Logger logger =
\r
50 LoggerFactory.getLogger(SynchronousTestInstanceCallable.class);
\r
51 private final TestExecution parentTestExecution;
\r
52 private final TestExecutionRepository testExecutionRepository;
\r
53 private final WorkflowProcessor processor;
\r
54 private final MongoTemplate mongoOperation;
\r
56 private final WorkflowRequest request;
\r
57 private String processInstanceId;
\r
59 public SynchronousTestInstanceCallable(
\r
60 WorkflowRequest request,
\r
61 TestExecution parentTestExecution,
\r
62 TestExecutionRepository testExecutionRepository,
\r
63 WorkflowProcessor processor,
\r
64 MongoTemplate mongoOperation) {
\r
65 this.request = request;
\r
66 this.parentTestExecution = parentTestExecution;
\r
68 this.processInstanceId = "";
\r
70 this.testExecutionRepository = testExecutionRepository;
\r
71 this.processor = processor;
\r
72 this.mongoOperation = mongoOperation;
\r
75 public SynchronousTestInstanceCallable(
\r
76 WorkflowRequest request,
\r
77 TestExecutionRepository testExecutionRepository,
\r
78 WorkflowProcessor processor,
\r
79 MongoTemplate mongoOperation) {
\r
80 this.request = request;
\r
81 this.parentTestExecution = null;
\r
83 this.processInstanceId = "";
\r
85 this.testExecutionRepository = testExecutionRepository;
\r
86 this.processor = processor;
\r
87 this.mongoOperation = mongoOperation;
\r
91 public TestExecution call() throws WorkflowProcessorException {
\r
93 TestExecution initialTestExecution = processor.processWorkflowRequest(request);
\r
94 this.processInstanceId = initialTestExecution.getProcessInstanceId();
\r
95 final Map<String, Boolean> abortionStatus = Collections.synchronizedMap(new HashMap<>());
\r
96 abortionStatus.put("isAborted", false);
\r
98 // Create a timer task that will call the cancellation after the specified time.
\r
99 TimerTask abortTestInstanceTask =
\r
102 public void run() {
\r
103 cancelProcessInstance(processInstanceId);
\r
104 abortionStatus.put("isAborted", true);
\r
108 // Start the daemon that waits the max time for a running test instance.
\r
109 long maxExecutionTimeInMillis = request.getMaxExecutionTimeInMillis();
\r
110 if (maxExecutionTimeInMillis > 0) {
\r
111 new Timer(true).schedule(abortTestInstanceTask, maxExecutionTimeInMillis);
\r
114 while (!isProcessInstanceEnded(processInstanceId)) {
\r
115 Thread.sleep(1000);
\r
118 // Find the result after the process instance after it has finished.
\r
119 TestExecution testExecution =
\r
120 testExecutionRepository.findFirstByProcessInstanceId(processInstanceId).orElse(null);
\r
121 if (testExecution == null) {
\r
124 "Process instance with id %s completed, however, a corresponding test execution was not found in the database.",
\r
125 processInstanceId));
\r
127 // If the test result was not set in the workflow, set it to completed now that we know the
\r
128 // process instance has finished executing.
\r
129 String testResult = testExecution.getTestResult();
\r
130 if (testResult.equalsIgnoreCase("UNKNOWN") || testResult.equalsIgnoreCase("STARTED")) {
\r
131 if (abortionStatus.get("isAborted")) {
\r
132 testExecution.setTestResult(ExecutionConstants.TestResult.TERMINATED);
\r
134 testExecution.setTestResult(ExecutionConstants.TestResult.COMPLETED);
\r
137 //TODO: RG remove prints
\r
138 System.out.println(testExecution.getTestHeadResults());
\r
139 System.out.println(request);
\r
140 TestExecutionUtility.saveTestResult(
\r
141 mongoOperation, testExecution, testExecution.getTestResult());
\r
144 // Saves the testExecution to the parent test execution if this belongs to a "sub" test
\r
146 saveToParentTestExecution(testExecution);
\r
149 return testExecution;
\r
150 } catch (WorkflowProcessorException e) {
\r
152 } catch (Exception e) {
\r
153 e.printStackTrace();
\r
158 private void saveToParentTestExecution(TestExecution testExecution) {
\r
159 if (parentTestExecution == null) {
\r
163 synchronized (parentTestExecution) {
\r
164 // Add the testExecution to the parentTestExecution
\r
165 parentTestExecution.getTestInstanceResults().add(testExecution);
\r
166 Query query = new Query();
\r
167 query.addCriteria(Criteria.where("_id").is(parentTestExecution.get_id()));
\r
168 // Also add businessKey as a criteria because the object won't be found if the business key
\r
169 // was somehow modified in the workflow.
\r
170 query.addCriteria(Criteria.where("businessKey").is(parentTestExecution.getBusinessKey()));
\r
171 Update update = new Update();
\r
172 update.set("testInstanceResults", parentTestExecution.getTestInstanceResults());
\r
173 UpdateResult result = mongoOperation.updateFirst(query, update, TestExecution.class);
\r
174 // Check the status of the findAndUpdate database, and appropriately handle the errors.
\r
175 if (result.getMatchedCount() == 0) {
\r
176 throw new TestExecutionException(
\r
178 "Unable to log the test result because a testExecution associated with _id, %s and businessKey %s, was not found.",
\r
179 parentTestExecution.get_id(), parentTestExecution.getBusinessKey()));
\r
180 } else if (result.getModifiedCount() == 0) {
\r
181 throw new TestExecutionException("Unable to persist the testExecution to the database.");
\r
186 "\t[Child-%s] finished saving result to parentTestExecution with result %s.",
\r
187 processInstanceId, testExecution.getTestResult()));
\r
190 private boolean isProcessInstanceEnded(String processInstanceId) {
\r
192 RuntimeService runtimeService =
\r
193 BpmPlatform.getProcessEngineService()
\r
194 .getProcessEngine(OtfCamundaConfiguration.processEngineName)
\r
195 .getRuntimeService();
\r
196 ProcessInstance processInstance =
\r
198 .createProcessInstanceQuery()
\r
199 .processInstanceId(processInstanceId)
\r
201 return processInstance == null || processInstance.isEnded();
\r
202 } catch (Exception e) {
\r
203 logger.error("Exception :", e);
\r
208 private boolean cancelProcessInstance(String processInstanceId) {
\r
210 RuntimeService runtimeService =
\r
211 BpmPlatform.getProcessEngineService()
\r
212 .getProcessEngine(OtfCamundaConfiguration.processEngineName)
\r
213 .getRuntimeService();
\r
214 runtimeService.deleteProcessInstance(
\r
215 processInstanceId, "Triggered by user defined max execution time timeout.");
\r
216 ProcessInstance processInstance =
\r
218 .createProcessInstanceQuery()
\r
219 .processInstanceId(processInstanceId)
\r
221 return processInstance == null || processInstance.isEnded();
\r
222 } catch (Exception e) {
\r
223 logger.error("Exception :", e);
\r