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;
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.fail;
27 import static org.onap.so.bpmn.core.json.JsonUtils.getJsonValue;
28 import static org.onap.so.bpmn.core.json.JsonUtils.updJsonValue;
29 import java.io.IOException;
30 import java.io.StringReader;
31 import java.lang.management.ManagementFactory;
32 import java.lang.management.RuntimeMXBean;
33 import java.util.ArrayList;
34 import java.util.HashMap;
35 import java.util.Iterator;
36 import java.util.List;
38 import java.util.UUID;
39 import javax.ws.rs.core.Response;
40 import javax.xml.bind.JAXBException;
41 import javax.xml.namespace.NamespaceContext;
42 import javax.xml.namespace.QName;
43 import javax.xml.parsers.DocumentBuilder;
44 import javax.xml.parsers.DocumentBuilderFactory;
45 import javax.xml.parsers.ParserConfigurationException;
46 import javax.xml.xpath.XPath;
47 import javax.xml.xpath.XPathConstants;
48 import javax.xml.xpath.XPathExpression;
49 import javax.xml.xpath.XPathExpressionException;
50 import javax.xml.xpath.XPathFactory;
51 import org.camunda.bpm.engine.HistoryService;
52 import org.camunda.bpm.engine.ProcessEngine;
53 import org.camunda.bpm.engine.ProcessEngineException;
54 import org.camunda.bpm.engine.RuntimeService;
55 import org.camunda.bpm.engine.history.HistoricProcessInstance;
56 import org.camunda.bpm.engine.history.HistoricVariableInstance;
57 import org.camunda.bpm.engine.runtime.ProcessInstance;
58 import org.camunda.bpm.engine.runtime.ProcessInstanceQuery;
59 import org.camunda.bpm.engine.test.ProcessEngineRule;
60 import org.camunda.bpm.engine.variable.impl.VariableMapImpl;
61 import org.custommonkey.xmlunit.DetailedDiff;
62 import org.custommonkey.xmlunit.XMLUnit;
63 import org.json.JSONArray;
64 import org.json.JSONObject;
65 import org.junit.Rule;
66 import org.onap.so.bpmn.common.adapter.sdnc.CallbackHeader;
67 import org.onap.so.bpmn.common.adapter.sdnc.SDNCAdapterCallbackRequest;
68 import org.onap.so.bpmn.common.adapter.sdnc.SDNCAdapterResponse;
69 import org.onap.so.bpmn.common.adapter.vnf.CreateVnfNotification;
70 import org.onap.so.bpmn.common.adapter.vnf.DeleteVnfNotification;
71 import org.onap.so.bpmn.common.adapter.vnf.MsoExceptionCategory;
72 import org.onap.so.bpmn.common.adapter.vnf.MsoRequest;
73 import org.onap.so.bpmn.common.adapter.vnf.UpdateVnfNotification;
74 import org.onap.so.bpmn.common.adapter.vnf.VnfRollback;
75 import org.onap.so.bpmn.common.workflow.context.WorkflowResponse;
76 import org.onap.so.bpmn.common.workflow.service.SDNCAdapterCallbackServiceImpl;
77 import org.onap.so.bpmn.common.workflow.service.VnfAdapterNotifyServiceImpl;
78 import org.onap.so.bpmn.common.workflow.service.WorkflowAsyncResource;
79 import org.onap.so.bpmn.common.workflow.service.WorkflowMessageResource;
80 import org.onap.so.bpmn.common.workflow.service.WorkflowResource;
81 import org.onap.so.bpmn.core.domain.Resource;
82 import org.onap.so.bpmn.core.domain.ServiceDecomposition;
83 import org.slf4j.Logger;
84 import org.slf4j.LoggerFactory;
85 import org.springframework.beans.factory.annotation.Autowired;
86 import org.w3c.dom.Document;
87 import org.w3c.dom.Element;
88 import org.w3c.dom.Node;
89 import org.w3c.dom.NodeList;
90 import org.xml.sax.InputSource;
91 import org.xml.sax.SAXException;
96 * A base class for Workflow tests.
98 * WireMock response transformers may be specified by declaring public static fields with the @WorkflowTestTransformer
99 * annotation. For example:
102 * @WorkflowTestTransformer
103 * public static final ResponseTransformer sdncAdapterMockTransformer = new SDNCAdapterMockTransformer();
107 public abstract class WorkflowTest {
109 private static final Logger logger = LoggerFactory.getLogger(WorkflowTest.class);
111 // TODO this is not used anymore, can maybe be removed
113 public ProcessEngineRule processEngineRule;
116 protected WorkflowResource workflowResourceSync;
119 protected ProcessEngine processEngine;
122 protected RuntimeService runtimeService;
125 protected HistoryService historyService;
128 private WorkflowAsyncResource workflowResource;
131 private WorkflowMessageResource workflowMessageResource;
134 SDNCAdapterCallbackServiceImpl callbackService;
136 * Content-Type for XML.
138 protected static final String XML = "application/xml";
141 * Content-Type for JSON.
143 protected static final String JSON = "application/json; charset=UTF-8";
145 private static final int timeout = 2000;
150 public WorkflowTest() throws RuntimeException {}
153 * The current request ID. Normally set when an "invoke" method is called.
155 protected volatile String msoRequestId = null;
158 * The current service instance ID. Normally set when an "invoke" method is called.
160 protected volatile String msoServiceInstanceId = null;
163 * Logs a test start method.
165 protected void logStart() {
166 logger.debug("STARTED TEST");
170 * Logs a test end method.
172 protected void logEnd() {
173 logger.debug("ENDED TEST");
177 * Invokes a subprocess.
179 * @param processKey the process key
180 * @param businessKey a unique key that will identify the process instance
181 * @param injectedVariables variables to inject into the process
183 protected void invokeSubProcess(String processKey, String businessKey, Map<String, Object> injectedVariables) {
184 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
185 List<String> arguments = runtimeMxBean.getInputArguments();
186 logger.debug("JVM args = {}", arguments);
188 msoRequestId = (String) injectedVariables.get("mso-request-id");
189 String requestId = (String) injectedVariables.get("msoRequestId");
191 if (msoRequestId == null && requestId == null) {
192 String msg = "mso-request-id variable was not provided";
197 // Note: some scenarios don't have a service-instance-id, may be null
198 msoServiceInstanceId = (String) injectedVariables.get("mso-service-instance-id");
201 runtimeService.startProcessInstanceByKey(processKey, businessKey, injectedVariables);
204 protected String invokeSubProcess(String processKey, Map<String, Object> injectedVariables) {
205 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
206 List<String> arguments = runtimeMxBean.getInputArguments();
207 logger.debug("JVM args = {}", arguments);
209 msoRequestId = (String) injectedVariables.get("mso-request-id");
210 String requestId = (String) injectedVariables.get("msoRequestId");
212 if (msoRequestId == null && requestId == null) {
213 String msg = "mso-request-id variable was not provided";
218 // Note: some scenarios don't have a service-instance-id, may be null
219 msoServiceInstanceId = (String) injectedVariables.get("mso-service-instance-id");
222 ProcessInstance processInstance =
223 runtimeService.startProcessInstanceByKey(processKey, msoRequestId, injectedVariables);
224 return processInstance.getId();
228 * Invokes an asynchronous process. Errors are handled with junit assertions and will cause the test to fail.
230 * @param processKey the process key
231 * @param schemaVersion the API schema version, e.g. "v1"
232 * @param businessKey a unique key that will identify the process instance
233 * @param request the request
234 * @return a TestAsyncResponse object associated with the test
235 * @throws InterruptedException
237 protected TestAsyncResponse invokeAsyncProcess(String processKey, String schemaVersion, String businessKey,
238 String request) throws InterruptedException {
239 return invokeAsyncProcess(processKey, schemaVersion, businessKey, request, null);
243 * Invokes an asynchronous process. Errors are handled with junit assertions and will cause the test to fail.
245 * @param processKey the process key
246 * @param schemaVersion the API schema version, e.g. "v1"
247 * @param businessKey a unique key that will identify the process instance
248 * @param request the request
249 * @param injectedVariables optional variables to inject into the process
250 * @return a TestAsyncResponse object associated with the test
251 * @throws InterruptedException
253 protected TestAsyncResponse invokeAsyncProcess(String processKey, String schemaVersion, String businessKey,
254 String request, Map<String, Object> injectedVariables) {
256 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
257 List<String> arguments = runtimeMxBean.getInputArguments();
258 logger.debug("JVM args = {}", arguments);
260 Map<String, Object> variables = createVariables(schemaVersion, businessKey, request, injectedVariables, false);
261 VariableMapImpl variableMapImpl = createVariableMapImpl(variables);
263 logger.debug("Sending {} to {} process", request, processKey);
265 TestAsyncResponse asyncResponse = new TestAsyncResponse();
267 asyncResponse.setResponse(workflowResource.startProcessInstanceByKey(processKey, variableMapImpl));
269 return asyncResponse;
273 * Invokes an asynchronous process. Errors are handled with junit assertions and will cause the test to fail.
275 * @param processKey the process key
276 * @param schemaVersion the API schema version, e.g. "v1"
277 * @param businessKey a unique key that will identify the process instance
278 * @param request the request
279 * @param injectedVariables optional variables to inject into the process
280 * @param serviceInstantiationModel indicates whether this method is being invoked for a flow that is designed using
281 * the service instantiation model
282 * @return a TestAsyncResponse object associated with the test
283 * @throws InterruptedException
285 protected Response invokeAsyncProcess(String processKey, String schemaVersion, String businessKey, String request,
286 Map<String, Object> injectedVariables, boolean serviceInstantiationModel) {
288 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
289 List<String> arguments = runtimeMxBean.getInputArguments();
290 logger.debug("JVM args = {}", arguments);
292 Map<String, Object> variables =
293 createVariables(schemaVersion, businessKey, request, injectedVariables, serviceInstantiationModel);
294 VariableMapImpl variableMapImpl = createVariableMapImpl(variables);
296 logger.debug("Sending {} to {} process", request, processKey);
298 return workflowResource.startProcessInstanceByKey(processKey, variableMapImpl);
303 * Private helper method that creates a variable map for a request. Errors are handled with junit assertions and
304 * will cause the test to fail.
306 * @param schemaVersion the API schema version, e.g. "v1"
307 * @param businessKey a unique key that will identify the process instance
308 * @param request the request
309 * @param injectedVariables optional variables to inject into the process
310 * @param serviceInstantiationModel indicates whether this method is being invoked for a flow that is designed using
311 * the service instantiation model
312 * @return a variable map
314 private Map<String, Object> createVariables(String schemaVersion, String businessKey, String request,
315 Map<String, Object> injectedVariables, boolean serviceInstantiationModel) {
317 Map<String, Object> variables = new HashMap<>();
319 // These variables may be overridded by injected variables.
320 variables.put("mso-service-request-timeout", "180");
321 variables.put("isDebugLogEnabled", "true");
323 // These variables may not be overridded by injected variables.
324 String[] notAllowed = new String[] {"mso-schema-version", "mso-business-key", "bpmnRequest", "mso-request-id",
325 "mso-service-instance-id"};
327 if (injectedVariables != null) {
328 for (String key : injectedVariables.keySet()) {
329 for (String var : notAllowed) {
330 if (var.equals(key)) {
331 String msg = "Cannot specify " + var + " in injected variables";
337 variables.put(key, injectedVariables.get(key));
341 variables.put("mso-schema-version", schemaVersion);
342 variables.put("mso-business-key", businessKey);
343 variables.put("bpmnRequest", request);
345 if (serviceInstantiationModel) {
348 * The request ID and the service instance ID are generated for flows that follow the service instantiation
349 * model unless "requestId" and "serviceInstanceId" are injected variables.
353 msoRequestId = (String) injectedVariables.get("requestId");
354 variables.put("mso-request-id", msoRequestId);
355 msoServiceInstanceId = (String) injectedVariables.get("serviceInstanceId");
356 variables.put("mso-service-instance-id", msoServiceInstanceId);
357 } catch (Exception e) {
359 if (msoRequestId == null || msoRequestId.trim().equals("")) {
360 logger.debug("No requestId element in injectedVariables");
361 variables.put("mso-request-id", UUID.randomUUID().toString());
363 if (msoServiceInstanceId == null || msoServiceInstanceId.trim().equals("")) {
364 logger.debug("No seviceInstanceId element in injectedVariables");
365 variables.put("mso-service-instance-id", UUID.randomUUID().toString());
369 msoRequestId = getXMLTextElement(request, "request-id");
371 if (msoRequestId == null) {
372 // check in injected variables
374 msoRequestId = (String) injectedVariables.get("requestId");
375 } catch (Exception e) {
377 if (msoRequestId == null || msoRequestId.trim().equals("")) {
378 String msg = "No request-id element in " + request;
384 variables.put("mso-request-id", msoRequestId);
386 // Note: some request types don't have a service-instance-id
387 msoServiceInstanceId = getXMLTextElement(request, "service-instance-id");
389 if (msoServiceInstanceId != null) {
390 variables.put("mso-service-instance-id", msoServiceInstanceId);
398 * Private helper method that creates a camunda VariableMapImpl from a simple variable map.
400 * @param variables the simple variable map
401 * @return a VariableMap
403 private VariableMapImpl createVariableMapImpl(Map<String, Object> variables) {
404 Map<String, Object> wrappedVariables = new HashMap<>();
406 for (String key : variables.keySet()) {
407 Object value = variables.get(key);
408 wrappedVariables.put(key, wrapVariableValue(value));
411 VariableMapImpl variableMapImpl = new VariableMapImpl();
412 variableMapImpl.put("variables", wrappedVariables);
413 return variableMapImpl;
417 * Private helper method that wraps a variable value for inclusion in a camunda VariableMapImpl.
419 * @param value the variable value
420 * @return the wrapped variable
422 private Map<String, Object> wrapVariableValue(Object value) {
423 HashMap<String, Object> valueMap = new HashMap<>();
424 valueMap.put("value", value);
429 * Receives a response from an asynchronous process. Errors are handled with junit assertions and will cause the
432 * @param businessKey the process business key
433 * @param asyncResponse the TestAsyncResponse object associated with the test
434 * @param timeout the timeout in milliseconds
435 * @return the WorkflowResponse
437 protected WorkflowResponse receiveResponse(String businessKey, TestAsyncResponse asyncResponse, long timeout) {
438 logger.debug("Waiting {}ms for process with business key {} to send a response", timeout, businessKey);
440 long now = System.currentTimeMillis() + timeout;
441 long endTime = now + timeout;
443 while (now <= endTime) {
444 Response response = asyncResponse.getResponse();
446 if (response != null) {
447 logger.debug("Received a response from process with business key {}", businessKey);
449 Object entity = response.getEntity();
451 if (!(entity instanceof WorkflowResponse)) {
452 String msg = "Response entity is " + (entity == null ? "null" : entity.getClass().getName())
453 + ", expected WorkflowResponse";
456 return null; // unreachable
459 return (WorkflowResponse) entity;
464 } catch (InterruptedException e) {
465 String msg = "Interrupted waiting for a response from process with business key " + businessKey;
468 return null; // unreachable
471 now = System.currentTimeMillis();
474 String msg = "No response received from process with business key " + businessKey + " within " + timeout + "ms";
476 fail("Process with business key " + businessKey + " did not end within 10000ms");
477 return null; // unreachable
481 * Runs a program to inject SDNC callback data into the test environment. A program is essentially just a list of
482 * keys that identify callback data to be injected, in sequence. An example program:
485 * reserve, assign, delete:ERR
488 * Errors are handled with junit assertions and will cause the test to fail.
490 * @param callbacks an object containing callback data for the program
491 * @param program the program to execute
493 protected void injectSDNCRestCallbacks(CallbackSet callbacks, String program) {
495 String[] cmds = program.replaceAll("\\s+", "").split(",");
497 for (String cmd : cmds) {
499 String modifier = "STD";
501 if (cmd.contains(":")) {
502 String[] parts = cmd.split(":");
507 String content = null;
508 String contentType = null;
510 if ("STD".equals(modifier)) {
511 CallbackData callbackData = callbacks.get(action);
513 if (callbackData == null) {
514 String msg = "No callback defined for '" + action + "' SDNC request";
519 content = callbackData.getContent();
520 contentType = callbackData.getContentType();
521 } else if ("ERR".equals(modifier)) {
523 "{\"SDNCServiceError\":{\"sdncRequestId\":\"((REQUEST-ID))\",\"responseCode\":\"500\",\"responseMessage\":\"SIMULATED ERROR FROM SDNC ADAPTER\",\"ackFinalIndicator\":\"Y\"}}";
526 String msg = "Invalid SDNC program modifier: '" + modifier + "'";
531 if (contentType == null) {
532 // Default for backward compatibility with existing tests.
536 if (!injectSDNCRestCallback(contentType, content, 10000)) {
537 fail("Failed to inject SDNC '" + action + "' callback");
542 } catch (InterruptedException e) {
543 fail("Interrupted after injection of SDNC '" + action + "' callback");
549 * Runs a program to inject SDNC events into the test environment. A program is essentially just a list of keys that
550 * identify event data to be injected, in sequence. An example program:
556 * NOTE: Each callback must have a message type associated with it, e.g. "SDNCAEvent". Errors are handled with junit
557 * assertions and will cause the test to fail.
559 * @param callbacks an object containing event data for the program
560 * @param program the program to execute
562 protected void injectSDNCEvents(CallbackSet callbacks, String program) {
563 injectWorkflowMessages(callbacks, program);
567 * Runs a program to inject SDNC callback data into the test environment. A program is essentially just a list of
568 * keys that identify callback data to be injected, in sequence. An example program:
571 * reserve, assign, delete:ERR
574 * Errors are handled with junit assertions and will cause the test to fail. Uses the static/default timeout value
575 * for backward compatibility.
577 * @param callbacks an object containing callback data for the program
578 * @param program the program to execute
580 protected void injectSDNCCallbacks(CallbackSet callbacks, String program) {
581 injectSDNCCallbacks(callbacks, program, timeout);
585 * Runs a program to inject SDNC callback data into the test environment. A program is essentially just a list of
586 * keys that identify callback data to be injected, in sequence. An example program:
589 * reserve, assign, delete:ERR
592 * Errors are handled with junit assertions and will cause the test to fail.
594 * @param callbacks an object containing callback data for the program
595 * @param program the program to execute
596 * @param timeout a timeout value to wait for the callback
598 protected void injectSDNCCallbacks(CallbackSet callbacks, String program, int timeout) {
600 String[] cmds = program.replaceAll("\\s+", "").split(",");
602 for (String cmd : cmds) {
604 String modifier = "STD";
606 if (cmd.contains(":")) {
607 String[] parts = cmd.split(":");
612 String content = null;
614 String respMsg = "OK";
616 if ("STD".equals(modifier)) {
617 CallbackData callbackData = callbacks.get(action);
619 if (callbackData == null) {
620 String msg = "No callback defined for '" + action + "' SDNC request";
625 content = callbackData.getContent();
628 } else if ("CREATED".equals(modifier)) {
629 CallbackData callbackData = callbacks.get(action);
631 if (callbackData == null) {
632 String msg = "No callback defined for '" + action + "' SDNC request";
637 content = callbackData.getContent();
640 } else if ("ERR".equals(modifier)) {
642 "<svc-request-id>((REQUEST-ID))</svc-request-id><response-code>500</response-code><response-message>SIMULATED ERROR FROM SDNC ADAPTER</response-message>";
644 respMsg = "SERVER ERROR";
646 String msg = "Invalid SDNC program modifier: '" + modifier + "'";
651 if (!injectSDNCCallback(respCode, respMsg, content, 10000)) {
652 fail("Failed to inject SDNC '" + action + "' callback");
657 } catch (InterruptedException e) {
658 fail("Interrupted after injection of SDNC '" + action + "' callback");
664 * Runs a program to inject VNF adapter REST callback data into the test environment. A program is essentially just
665 * a list of keys that identify callback data to be injected, in sequence. An example program:
671 * Errors are handled with junit assertions and will cause the test to fail.
673 * @param callbacks an object containing callback data for the program
674 * @param program the program to execute
676 protected void injectVNFRestCallbacks(CallbackSet callbacks, String program) {
678 String[] cmds = program.replaceAll("\\s+", "").split(",");
680 for (String cmd : cmds) {
682 String modifier = "STD";
684 if (cmd.contains(":")) {
685 String[] parts = cmd.split(":");
690 String content = null;
691 String contentType = null;
693 if ("STD".equals(modifier)) {
694 CallbackData callbackData = callbacks.get(action);
696 if (callbackData == null) {
697 String msg = "No callback defined for '" + action + "' VNF REST request";
702 content = callbackData.getContent();
703 contentType = callbackData.getContentType();
704 } else if ("ERR".equals(modifier)) {
705 content = "SIMULATED ERROR FROM VNF ADAPTER";
706 contentType = "text/plain";
708 String msg = "Invalid VNF REST program modifier: '" + modifier + "'";
713 if (contentType == null) {
714 // Default for backward compatibility with existing tests.
718 if (!injectVnfAdapterRestCallback(contentType, content, 10000)) {
719 fail("Failed to inject VNF REST '" + action + "' callback");
724 } catch (InterruptedException e) {
725 fail("Interrupted after injection of VNF REST '" + action + "' callback");
731 * Runs a program to inject VNF callback data into the test environment. A program is essentially just a list of
732 * keys that identify callback data to be injected, in sequence. An example program:
735 * createVnf, deleteVnf
738 * Errors are handled with junit assertions and will cause the test to fail.
740 * @param callbacks an object containing callback data for the program
741 * @param program the program to execute
743 protected void injectVNFCallbacks(CallbackSet callbacks, String program) {
745 String[] cmds = program.replaceAll("\\s+", "").split(",");
747 for (String cmd : cmds) {
749 String modifier = "STD";
751 if (cmd.contains(":")) {
752 String[] parts = cmd.split(":");
757 String content = null;
759 if ("STD".equals(modifier)) {
760 CallbackData callbackData = callbacks.get(action);
762 if (callbackData == null) {
763 String msg = "No callback defined for '" + action + "' VNF request";
768 content = callbackData.getContent();
769 } else if ("ERR".equals(modifier)) {
770 String msg = "Currently unsupported VNF program modifier: '" + modifier + "'";
774 String msg = "Invalid VNF program modifier: '" + modifier + "'";
779 boolean injected = false;
781 if (content.contains("createVnfNotification")) {
782 injected = injectCreateVNFCallback(content, 10000);
783 } else if (content.contains("deleteVnfNotification")) {
784 injected = injectDeleteVNFCallback(content, 10000);
785 } else if (content.contains("updateVnfNotification")) {
786 injected = injectUpdateVNFCallback(content, 10000);
790 String msg = "Failed to inject VNF '" + action + "' callback";
797 } catch (InterruptedException e) {
798 fail("Interrupted after injection of VNF '" + action + "' callback");
804 * Waits for the number of running processes with the specified process definition key to equal a particular count.
806 * @param processKey the process definition key
807 * @param count the desired count
808 * @param timeout the timeout in milliseconds
810 protected void waitForRunningProcessCount(String processKey, int count, long timeout) {
811 logger.debug("Waiting {}ms for there to be {} {} instances", timeout, count, processKey);
813 long now = System.currentTimeMillis() + timeout;
814 long endTime = now + timeout;
817 while (now <= endTime) {
818 int actual = runtimeService.createProcessInstanceQuery().processDefinitionKey(processKey).list().size();
820 if (actual != last) {
821 logger.debug("There are now {} {} instances", actual, processKey);
825 if (actual == count) {
831 } catch (InterruptedException e) {
832 String msg = "Interrupted waiting for there to be " + count + " " + processKey + " instances";
837 now = System.currentTimeMillis();
840 String msg = "Timed out waiting for there to be " + count + " " + processKey + " instances";
846 * Waits for the specified process variable to be set.
848 * @param processKey the process definition key
849 * @param variable the variable name
850 * @param timeout the timeout in milliseconds
851 * @return the variable value, or null if it cannot be obtained in the specified time
853 protected Object getProcessVariable(String processKey, String variable, long timeout) {
855 logger.debug("Waiting " + timeout + "ms for " + processKey + "." + variable + " to be set");
857 long now = System.currentTimeMillis() + timeout;
858 long endTime = now + timeout;
860 ProcessInstance processInstance = null;
863 while (value == null) {
865 if (processInstance == null) {
866 logger.debug("Timed out waiting for " + processKey + " to start");
868 logger.debug("Timed out waiting for " + processKey + "[" + processInstance.getId() + "]." + variable
875 ProcessInstanceQuery processInstanceQuery = null;
876 if (processInstance == null) {
877 processInstanceQuery = runtimeService.createProcessInstanceQuery().processDefinitionKey(processKey);
880 if (processInstanceQuery.count() == 1 || processInstanceQuery.count() == 0) {
881 processInstance = processInstanceQuery.singleResult();
883 // TODO There shouldnt be more than one in the list but seems to be happening, need to figure out why
884 // happening and best way to get correct one from list
885 logger.debug("Process Instance Query returned {} instance. Getting the last instance in the list",
886 processInstanceQuery.count());
887 List<ProcessInstance> processList = processInstanceQuery.list();
888 processInstance = processList.get((processList.size() - 1));
892 if (processInstance != null) {
893 value = runtimeService.getVariable(processInstance.getId(), variable);
898 } catch (InterruptedException e) {
899 logger.debug("Interrupted waiting for {}.{} to be set", processKey, variable);
903 now = System.currentTimeMillis();
906 logger.debug(processKey + "[" + processInstance.getId() + "]." + variable + "=" + value);
912 * Injects a single SDNC adapter callback request. The specified callback data may contain the placeholder string
913 * ((REQUEST-ID)) which is replaced with the actual SDNC request ID. Note: this is not the requestId in the original
916 * @param contentType the HTTP content type for the callback
917 * @param content the content of the callback
918 * @param timeout the timeout in milliseconds
919 * @return true if the callback could be injected, false otherwise
921 protected boolean injectSDNCRestCallback(String contentType, String content, long timeout) {
922 String sdncRequestId = (String) getProcessVariable("SDNCAdapterRestV1", "SDNCAResponse_CORRELATOR", timeout);
924 if (sdncRequestId == null) {
925 sdncRequestId = (String) getProcessVariable("SDNCAdapterRestV2", "SDNCAResponse_CORRELATOR", timeout);
928 if (sdncRequestId == null) {
932 content = content.replace("((REQUEST-ID))", sdncRequestId);
933 // Deprecated usage. All test code should switch to the (( ... )) syntax.
934 content = content.replace("{{REQUEST-ID}}", sdncRequestId);
936 logger.debug("Injecting SDNC adapter callback");
938 Response response = workflowMessageResource.deliver(contentType, "SDNCAResponse", sdncRequestId, content);
939 logger.debug("Workflow response to SDNC adapter callback: " + response);
944 * Injects a single SDNC adapter callback request. The specified callback data may contain the placeholder string
945 * ((REQUEST-ID)) which is replaced with the actual SDNC request ID. Note: this is not the requestId in the original
948 * @param content the content of the callback
949 * @param respCode the response code (normally 200)
950 * @param respMsg the response message (normally "OK")
951 * @param timeout the timeout in milliseconds
952 * @return true if the callback could be injected, false otherwise
954 protected boolean injectSDNCCallback(int respCode, String respMsg, String content, long timeout) {
956 String sdncRequestId = (String) getProcessVariable("sdncAdapter", "SDNCA_requestId", timeout);
958 if (sdncRequestId == null) {
962 content = content.replace("((REQUEST-ID))", sdncRequestId);
963 // Deprecated usage. All test code should switch to the (( ... )) syntax.
964 content = content.replace("{{REQUEST-ID}}", sdncRequestId);
966 // TODO this needs to be fixed. It is causing double tags and content
967 // Need to parse content before setting below since content includes not just RequestData or modify callback
968 // files to only contain RequestData contents.
970 logger.debug("Injecting SDNC adapter callback");
971 CallbackHeader callbackHeader = new CallbackHeader();
972 callbackHeader.setRequestId(sdncRequestId);
973 callbackHeader.setResponseCode(String.valueOf(respCode));
974 callbackHeader.setResponseMessage(respMsg);
975 SDNCAdapterCallbackRequest sdncAdapterCallbackRequest = new SDNCAdapterCallbackRequest();
976 sdncAdapterCallbackRequest.setCallbackHeader(callbackHeader);
977 sdncAdapterCallbackRequest.setRequestData(content);
978 SDNCAdapterResponse sdncAdapterResponse = callbackService.sdncAdapterCallback(sdncAdapterCallbackRequest);
979 logger.debug("Workflow response to SDNC adapter callback: " + sdncAdapterResponse);
985 * Injects a single VNF adapter callback request. The specified callback data may contain the placeholder string
986 * ((MESSAGE-ID)) which is replaced with the actual message ID. Note: this is not the requestId in the original MSO
989 * @param contentType the HTTP content type for the callback
990 * @param content the content of the callback
991 * @param timeout the timeout in milliseconds
992 * @return true if the callback could be injected, false otherwise
994 protected boolean injectVnfAdapterRestCallback(String contentType, String content, long timeout) {
995 String messageId = (String) getProcessVariable("vnfAdapterRestV1", "VNFAResponse_CORRELATOR", timeout);
997 if (messageId == null) {
1001 content = content.replace("((MESSAGE-ID))", messageId);
1002 // Deprecated usage. All test code should switch to the (( ... )) syntax.
1003 content = content.replace("{{MESSAGE-ID}}", messageId);
1005 logger.debug("Injecting VNF adapter callback");
1007 Response response = workflowMessageResource.deliver(contentType, "VNFAResponse", messageId, content);
1008 logger.debug("Workflow response to VNF adapter callback: {}", response);
1013 * Injects a Create VNF adapter callback request. The specified callback data may contain the placeholder string
1014 * ((MESSAGE-ID)) which is replaced with the actual message ID. It may also contain the placeholder string
1015 * ((REQUEST-ID)) which is replaced request ID of the original MSO request.
1017 * @param content the content of the callback
1018 * @param timeout the timeout in milliseconds
1019 * @return true if the callback could be injected, false otherwise
1020 * @throws JAXBException if the content does not adhere to the schema
1022 protected boolean injectCreateVNFCallback(String content, long timeout) {
1024 String messageId = (String) getProcessVariable("vnfAdapterCreateV1", "VNFC_messageId", timeout);
1026 if (messageId == null) {
1030 content = content.replace("((MESSAGE-ID))", messageId);
1031 // Deprecated usage. All test code should switch to the (( ... )) syntax.
1032 content = content.replace("{{MESSAGE-ID}}", messageId);
1034 if (content.contains("((REQUEST-ID))")) {
1035 content = content.replace("((REQUEST-ID))", msoRequestId);
1036 // Deprecated usage. All test code should switch to the (( ... )) syntax.
1037 content = content.replace("{{REQUEST-ID}}", msoRequestId);
1040 logger.debug("Injecting VNF adapter callback");
1042 // Is it possible to unmarshal this with JAXB? I couldn't.
1044 CreateVnfNotification createVnfNotification = new CreateVnfNotification();
1045 XPathTool xpathTool = new VnfNotifyXPathTool();
1046 xpathTool.setXML(content);
1049 String completed = xpathTool.evaluate("/tns:createVnfNotification/tns:completed/text()");
1050 createVnfNotification.setCompleted("true".equals(completed));
1052 String vnfId = xpathTool.evaluate("/tns:createVnfNotification/tns:vnfId/text()");
1053 createVnfNotification.setVnfId(vnfId);
1055 NodeList entries = (NodeList) xpathTool.evaluate("/tns:createVnfNotification/tns:outputs/tns:entry",
1056 XPathConstants.NODESET);
1058 CreateVnfNotificationOutputs outputs = new CreateVnfNotificationOutputs();
1060 for (int i = 0; i < entries.getLength(); i++) {
1061 Node node = entries.item(i);
1063 if (node.getNodeType() == Node.ELEMENT_NODE) {
1064 Element entry = (Element) node;
1065 String key = entry.getElementsByTagNameNS("*", "key").item(0).getTextContent();
1066 String value = entry.getElementsByTagNameNS("*", "value").item(0).getTextContent();
1067 outputs.add(key, value);
1071 createVnfNotification.setOutputs(outputs);
1073 VnfRollback rollback = new VnfRollback();
1075 String cloudSiteId = xpathTool.evaluate("/tns:createVnfNotification/tns:rollback/tns:cloudSiteId/text()");
1076 rollback.setCloudSiteId(cloudSiteId);
1078 String cloudOwner = xpathTool.evaluate("/tns:createVnfNotification/tns:rollback/tns:cloudOwner/text()");
1079 rollback.setCloudOwner(cloudOwner);
1082 xpathTool.evaluate("/tns:createVnfNotification/tns:rollback/tns:msoRequest/tns:requestId/text()");
1083 String serviceInstanceId = xpathTool
1084 .evaluate("/tns:createVnfNotification/tns:rollback/tns:msoRequest/tns:serviceInstanceId/text()");
1086 if (requestId != null || serviceInstanceId != null) {
1087 MsoRequest msoRequest = new MsoRequest();
1088 msoRequest.setRequestId(requestId);
1089 msoRequest.setServiceInstanceId(serviceInstanceId);
1090 rollback.setMsoRequest(msoRequest);
1093 String tenantCreated =
1094 xpathTool.evaluate("/tns:createVnfNotification/tns:rollback/tns:tenantCreated/text()");
1095 rollback.setTenantCreated("true".equals(tenantCreated));
1097 String tenantId = xpathTool.evaluate("/tns:createVnfNotification/tns:rollback/tns:tenantId/text()");
1098 rollback.setTenantId(tenantId);
1100 String vnfCreated = xpathTool.evaluate("/tns:createVnfNotification/tns:rollback/tns:vnfCreated/text()");
1101 rollback.setVnfCreated("true".equals(vnfCreated));
1103 String rollbackVnfId = xpathTool.evaluate("/tns:createVnfNotification/tns:rollback/tns:vnfId/text()");
1104 rollback.setVnfId(rollbackVnfId);
1106 createVnfNotification.setRollback(rollback);
1108 } catch (Exception e) {
1109 logger.debug("Failed to unmarshal VNF callback content:");
1110 logger.debug(content);
1114 VnfAdapterNotifyServiceImpl notifyService = new VnfAdapterNotifyServiceImpl();
1117 notifyService.createVnfNotification(messageId, createVnfNotification.isCompleted(),
1118 createVnfNotification.getException(), createVnfNotification.getErrorMessage(),
1119 createVnfNotification.getVnfId(), createVnfNotification.getOutputs(),
1120 createVnfNotification.getRollback());
1126 * Injects a Delete VNF adapter callback request. The specified callback data may contain the placeholder string
1127 * ((MESSAGE-ID)) which is replaced with the actual message ID. It may also contain the placeholder string
1128 * ((REQUEST-ID)) which is replaced request ID of the original MSO request.
1130 * @param content the content of the callback
1131 * @param timeout the timeout in milliseconds
1132 * @return true if the callback could be injected, false otherwise
1133 * @throws JAXBException if the content does not adhere to the schema
1135 protected boolean injectDeleteVNFCallback(String content, long timeout) {
1137 String messageId = (String) getProcessVariable("vnfAdapterDeleteV1", "VNFDEL_uuid", timeout);
1139 if (messageId == null) {
1143 content = content.replace("((MESSAGE-ID))", messageId);
1144 // Deprecated usage. All test code should switch to the (( ... )) syntax.
1145 content = content.replace("{{MESSAGE-ID}}", messageId);
1147 logger.debug("Injecting VNF adapter delete callback");
1149 // Is it possible to unmarshal this with JAXB? I couldn't.
1151 DeleteVnfNotification deleteVnfNotification = new DeleteVnfNotification();
1152 XPathTool xpathTool = new VnfNotifyXPathTool();
1153 xpathTool.setXML(content);
1156 String completed = xpathTool.evaluate("/tns:deleteVnfNotification/tns:completed/text()");
1157 deleteVnfNotification.setCompleted("true".equals(completed));
1158 // if notification failure, set the exception and error message
1159 if (deleteVnfNotification.isCompleted() == false) {
1160 deleteVnfNotification.setException(MsoExceptionCategory.INTERNAL);
1161 deleteVnfNotification
1162 .setErrorMessage(xpathTool.evaluate("/tns:deleteVnfNotification/tns:errorMessage/text()"));
1165 } catch (Exception e) {
1166 logger.debug("Failed to unmarshal VNF Delete callback content:");
1167 logger.debug(content);
1171 VnfAdapterNotifyServiceImpl notifyService = new VnfAdapterNotifyServiceImpl();
1174 notifyService.deleteVnfNotification(messageId, deleteVnfNotification.isCompleted(),
1175 deleteVnfNotification.getException(), deleteVnfNotification.getErrorMessage());
1181 * Injects a Update VNF adapter callback request. The specified callback data may contain the placeholder string
1182 * ((MESSAGE-ID)) which is replaced with the actual message ID. It may also contain the placeholder string
1183 * ((REQUEST-ID)) which is replaced request ID of the original MSO request.
1185 * @param content the content of the callback
1186 * @param timeout the timeout in milliseconds
1187 * @return true if the callback could be injected, false otherwise
1188 * @throws JAXBException if the content does not adhere to the schema
1190 protected boolean injectUpdateVNFCallback(String content, long timeout) {
1192 String messageId = (String) getProcessVariable("vnfAdapterUpdate", "VNFU_messageId", timeout);
1194 if (messageId == null) {
1198 content = content.replace("((MESSAGE-ID))", messageId);
1199 // Deprecated usage. All test code should switch to the (( ... )) syntax.
1200 content = content.replace("{{MESSAGE-ID}}", messageId);
1202 content = content.replace("((REQUEST-ID))", msoRequestId);
1203 // Deprecated usage. All test code should switch to the (( ... )) syntax.
1204 content = content.replace("{{REQUEST-ID}}", msoRequestId);
1206 logger.debug("Injecting VNF adapter callback");
1208 // Is it possible to unmarshal this with JAXB? I couldn't.
1210 UpdateVnfNotification updateVnfNotification = new UpdateVnfNotification();
1211 XPathTool xpathTool = new VnfNotifyXPathTool();
1212 xpathTool.setXML(content);
1215 String completed = xpathTool.evaluate("/tns:updateVnfNotification/tns:completed/text()");
1216 updateVnfNotification.setCompleted("true".equals(completed));
1218 NodeList entries = (NodeList) xpathTool.evaluate("/tns:updateVnfNotification/tns:outputs/tns:entry",
1219 XPathConstants.NODESET);
1221 UpdateVnfNotificationOutputs outputs = new UpdateVnfNotificationOutputs();
1223 for (int i = 0; i < entries.getLength(); i++) {
1224 Node node = entries.item(i);
1226 if (node.getNodeType() == Node.ELEMENT_NODE) {
1227 Element entry = (Element) node;
1228 String key = entry.getElementsByTagNameNS("*", "key").item(0).getTextContent();
1229 String value = entry.getElementsByTagNameNS("*", "value").item(0).getTextContent();
1230 outputs.add(key, value);
1234 updateVnfNotification.setOutputs(outputs);
1236 VnfRollback rollback = new VnfRollback();
1238 String cloudSiteId = xpathTool.evaluate("/tns:updateVnfNotification/tns:rollback/tns:cloudSiteId/text()");
1239 rollback.setCloudSiteId(cloudSiteId);
1241 String cloudOwner = xpathTool.evaluate("/tns:updateVnfNotification/tns:rollback/tns:cloudOwner/text()");
1242 rollback.setCloudOwner(cloudOwner);
1245 xpathTool.evaluate("/tns:updateVnfNotification/tns:rollback/tns:msoRequest/tns:requestId/text()");
1246 String serviceInstanceId = xpathTool
1247 .evaluate("/tns:updateVnfNotification/tns:rollback/tns:msoRequest/tns:serviceInstanceId/text()");
1249 if (requestId != null || serviceInstanceId != null) {
1250 MsoRequest msoRequest = new MsoRequest();
1251 msoRequest.setRequestId(requestId);
1252 msoRequest.setServiceInstanceId(serviceInstanceId);
1253 rollback.setMsoRequest(msoRequest);
1256 String tenantCreated =
1257 xpathTool.evaluate("/tns:updateVnfNotification/tns:rollback/tns:tenantCreated/text()");
1258 rollback.setTenantCreated("true".equals(tenantCreated));
1260 String tenantId = xpathTool.evaluate("/tns:updateVnfNotification/tns:rollback/tns:tenantId/text()");
1261 rollback.setTenantId(tenantId);
1263 String vnfCreated = xpathTool.evaluate("/tns:updateVnfNotification/tns:rollback/tns:vnfCreated/text()");
1264 rollback.setVnfCreated("true".equals(vnfCreated));
1266 String rollbackVnfId = xpathTool.evaluate("/tns:updateVnfNotification/tns:rollback/tns:vnfId/text()");
1267 rollback.setVnfId(rollbackVnfId);
1269 updateVnfNotification.setRollback(rollback);
1271 } catch (Exception e) {
1272 logger.debug("Failed to unmarshal VNF callback content:");
1273 logger.debug(content);
1277 VnfAdapterNotifyServiceImpl notifyService = new VnfAdapterNotifyServiceImpl();
1280 notifyService.updateVnfNotification(messageId, updateVnfNotification.isCompleted(),
1281 updateVnfNotification.getException(), updateVnfNotification.getErrorMessage(),
1282 updateVnfNotification.getOutputs(), updateVnfNotification.getRollback());
1288 * Runs a program to inject workflow messages into the test environment. A program is essentially just a list of
1289 * keys that identify event data to be injected, in sequence. An example program:
1295 * Errors are handled with junit assertions and will cause the test to fail. NOTE: Each callback must have a
1296 * workflow message type associated with it.
1298 * @param callbacks an object containing event data for the program
1299 * @param program the program to execute
1301 protected void injectWorkflowMessages(CallbackSet callbacks, String program) {
1303 String[] cmds = program.replaceAll("\\s+", "").split(",");
1305 for (String cmd : cmds) {
1306 String action = cmd;
1307 String modifier = "STD";
1309 if (cmd.contains(":")) {
1310 String[] parts = cmd.split(":");
1312 modifier = parts[1];
1315 String messageType = null;
1316 String content = null;
1317 String contentType = null;
1319 if ("STD".equals(modifier)) {
1320 CallbackData callbackData = callbacks.get(action);
1322 if (callbackData == null) {
1323 String msg = "No '" + action + "' workflow message callback is defined";
1328 messageType = callbackData.getMessageType();
1330 if (messageType == null || messageType.trim().equals("")) {
1331 String msg = "No workflow message type is defined in the '" + action + "' callback";
1336 content = callbackData.getContent();
1337 contentType = callbackData.getContentType();
1339 String msg = "Invalid workflow message program modifier: '" + modifier + "'";
1344 if (!injectWorkflowMessage(contentType, messageType, content, 10000)) {
1345 fail("Failed to inject '" + action + "' workflow message");
1350 } catch (InterruptedException e) {
1351 fail("Interrupted after injection of '" + action + "' workflow message");
1357 * Injects a workflow message. The specified callback data may contain the placeholder string ((CORRELATOR)) which
1358 * is replaced with the actual correlator value.
1360 * @param contentType the HTTP contentType for the message (possibly null)
1361 * @param messageType the message type
1362 * @param content the message content (possibly null)
1363 * @param timeout the timeout in milliseconds
1364 * @return true if the message could be injected, false otherwise
1366 protected boolean injectWorkflowMessage(String contentType, String messageType, String content, long timeout) {
1367 String correlator = (String) getProcessVariable("ReceiveWorkflowMessage", messageType + "_CORRELATOR", timeout);
1369 if (correlator == null) {
1373 if (content != null) {
1374 content = content.replace("((CORRELATOR))", correlator);
1377 logger.debug("Injecting " + messageType + " message");
1379 Response response = workflowMessageResource.deliver(contentType, messageType, correlator, content);
1380 logger.debug("Workflow response to {} message: {}", messageType, response);
1385 * Runs a program to inject sniro workflow messages into the test environment. A program is essentially just a list
1386 * of keys that identify event data to be injected, in sequence. For more details, see injectSNIROCallbacks(String
1387 * contentType, String messageType, String content, long timeout)
1389 * Errors are handled with junit assertions and will cause the test to fail. NOTE: Each callback must have a
1390 * workflow message type associated with it.
1392 * @param callbacks an object containing event data for the program
1393 * @param program the program to execute
1395 protected void injectSNIROCallbacks(CallbackSet callbacks, String program) {
1397 String[] cmds = program.replaceAll("\\s+", "").split(",");
1399 for (String cmd : cmds) {
1400 String action = cmd;
1401 String modifier = "STD";
1403 if (cmd.contains(":")) {
1404 String[] parts = cmd.split(":");
1406 modifier = parts[1];
1409 String messageType = null;
1410 String content = null;
1411 String contentType = null;
1413 if ("STD".equals(modifier)) {
1414 CallbackData callbackData = callbacks.get(action);
1416 if (callbackData == null) {
1417 String msg = "No '" + action + "' workflow message callback is defined";
1422 messageType = callbackData.getMessageType();
1424 if (messageType == null || messageType.trim().equals("")) {
1425 String msg = "No workflow message type is defined in the '" + action + "' callback";
1430 content = callbackData.getContent();
1431 contentType = callbackData.getContentType();
1433 String msg = "Invalid workflow message program modifier: '" + modifier + "'";
1438 if (!injectSNIROCallbacks(contentType, messageType, content, 10000)) {
1439 fail("Failed to inject '" + action + "' workflow message");
1444 } catch (InterruptedException e) {
1445 fail("Interrupted after injection of '" + action + "' workflow message");
1451 * Injects a sniro workflow message. The specified callback response may contain the placeholder strings
1452 * ((CORRELATOR)) and ((SERVICE_RESOURCE_ID)) The ((CORRELATOR)) is replaced with the actual correlator value from
1453 * the request. The ((SERVICE_RESOURCE_ID)) is replaced with the actual serviceResourceId value from the sniro
1454 * request. Currently this only works with sniro request that contain only 1 resource.
1456 * @param contentType the HTTP contentType for the message (possibly null)
1457 * @param messageType the message type
1458 * @param content the message content (possibly null)
1459 * @param timeout the timeout in milliseconds
1460 * @return true if the message could be injected, false otherwise
1462 protected boolean injectSNIROCallbacks(String contentType, String messageType, String content, long timeout) {
1463 String correlator = (String) getProcessVariable("ReceiveWorkflowMessage", messageType + "_CORRELATOR", timeout);
1465 if (correlator == null) {
1468 if (content != null) {
1469 content = content.replace("((CORRELATOR))", correlator);
1470 if (messageType.equalsIgnoreCase("SNIROResponse")) {
1471 ServiceDecomposition decomp =
1472 (ServiceDecomposition) getProcessVariable("Homing", "serviceDecomposition", timeout);
1473 List<Resource> resourceList = decomp.getServiceResources();
1474 if (resourceList.size() == 1) {
1475 String resourceId = "";
1476 for (Resource resource : resourceList) {
1477 resourceId = resource.getResourceId();
1479 String homingList = getJsonValue(content, "solutionInfo.placementInfo");
1480 JSONArray placementArr = null;
1482 placementArr = new JSONArray(homingList);
1483 } catch (Exception e) {
1486 if (placementArr.length() == 1) {
1487 content = content.replace("((SERVICE_RESOURCE_ID))", resourceId);
1489 String licenseInfoList = getJsonValue(content, "solutionInfo.licenseInfo");
1490 JSONArray licenseArr = null;
1492 licenseArr = new JSONArray(licenseInfoList);
1493 } catch (Exception e) {
1496 if (licenseArr.length() == 1) {
1497 content = content.replace("((SERVICE_RESOURCE_ID))", resourceId);
1501 String homingList = getJsonValue(content, "solutionInfo.placementInfo");
1502 String licenseInfoList = getJsonValue(content, "solutionInfo.licenseInfo");
1503 JSONArray placementArr = new JSONArray(homingList);
1504 JSONArray licenseArr = new JSONArray(licenseInfoList);
1505 for (Resource resource : resourceList) {
1506 String resourceModuleName = resource.getModelInfo().getModelInstanceName();
1507 String resourceId = resource.getResourceId();
1509 for (int i = 0; i < placementArr.length(); i++) {
1510 JSONObject placementObj = placementArr.getJSONObject(i);
1511 String placementModuleName = placementObj.getString("resourceModuleName");
1512 if (placementModuleName.equalsIgnoreCase(resourceModuleName)) {
1513 String placementString = placementObj.toString();
1514 placementString = placementString.replace("((SERVICE_RESOURCE_ID))", resourceId);
1515 JSONObject newPlacementObj = new JSONObject(placementString);
1516 placementArr.put(i, newPlacementObj);
1520 for (int i = 0; i < licenseArr.length(); i++) {
1521 JSONObject licenseObj = licenseArr.getJSONObject(i);
1522 String licenseModuleName = licenseObj.getString("resourceModuleName");
1523 if (licenseModuleName.equalsIgnoreCase(resourceModuleName)) {
1524 String licenseString = licenseObj.toString();
1525 licenseString = licenseString.replace("((SERVICE_RESOURCE_ID))", resourceId);
1526 JSONObject newLicenseObj = new JSONObject(licenseString);
1527 licenseArr.put(i, newLicenseObj);
1531 String newPlacementInfos = placementArr.toString();
1532 String newLicenseInfos = licenseArr.toString();
1533 content = updJsonValue(content, "solutionInfo.placementInfo", newPlacementInfos);
1534 content = updJsonValue(content, "solutionInfo.licenseInfo", newLicenseInfos);
1535 } catch (Exception e) {
1542 logger.debug("Injecting " + messageType + " message");
1544 Response response = workflowMessageResource.deliver(contentType, messageType, correlator, content);
1545 logger.debug("Workflow response to {} message: {}", messageType, response);
1551 * Wait for the process to end.
1553 * @param businessKey the process business key
1554 * @param timeout the amount of time to wait, in milliseconds
1556 protected void waitForProcessEnd(String businessKey, long timeout) {
1557 logger.debug("Waiting {}ms for process with business key {} to end", timeout, businessKey);
1559 long now = System.currentTimeMillis() + timeout;
1560 long endTime = now + timeout;
1562 while (now <= endTime) {
1563 if (isProcessEnded(businessKey)) {
1564 logger.debug("Process with business key {} has ended", businessKey);
1570 } catch (InterruptedException e) {
1571 String msg = "Interrupted waiting for process with business key " + businessKey + " to end";
1576 now = System.currentTimeMillis();
1579 String msg = "Process with business key " + businessKey + " did not end within " + timeout + "ms";
1585 * Wait for the process to end. Must be used when multiple process instances exist with this same business key such
1586 * as when its passed to subflows or shared across multiple processes.
1588 * @param businessKey the process business key
1589 * @param processName the process definition name
1590 * @param timeout the amount of time to wait, in milliseconds
1593 protected void waitForProcessEnd(String businessKey, String processName, long timeout) {
1594 logger.debug("Waiting {}ms for process with business key {} to end", timeout, businessKey);
1596 long now = System.currentTimeMillis() + timeout;
1597 long endTime = now + timeout;
1599 while (now <= endTime) {
1600 if (isProcessEnded(businessKey, processName)) {
1601 logger.debug("Process with business key {} has ended", businessKey);
1607 } catch (InterruptedException e) {
1608 String msg = "Interrupted waiting for process with business key " + businessKey + " to end";
1613 now = System.currentTimeMillis();
1616 String msg = "Process with business key " + businessKey + " did not end within " + timeout + "ms";
1622 * Verifies that the specified historic process variable has the specified value. If the variable does not have the
1623 * specified value, the test is failed.
1625 * @param businessKey the process business key
1626 * @param variable the variable name
1627 * @param value the expected variable value
1629 protected void checkVariable(String businessKey, String variable, Object value) {
1630 if (!isProcessEnded(businessKey)) {
1631 fail("Cannot get historic variable " + variable + " because process with business key " + businessKey
1632 + " has not ended");
1635 Object variableValue = getVariableFromHistory(businessKey, variable);
1636 assertEquals(value, variableValue);
1640 * Checks to see if the specified process is ended.
1642 * @param businessKey the process business Key
1643 * @return true if the process is ended
1645 protected boolean isProcessEnded(String businessKey) {
1646 HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
1647 .processInstanceBusinessKey(businessKey).singleResult();
1648 return processInstance != null && processInstance.getEndTime() != null;
1652 * Checks to see if the specified process is ended.
1654 * @param processInstanceId the process Instance Id
1655 * @return true if the process is ended
1657 protected boolean isProcessEndedByProcessInstanceId(String processInstanceId) {
1658 HistoricProcessInstance processInstance =
1659 historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
1660 return processInstance != null && processInstance.getEndTime() != null;
1664 * Checks to see if the specified process is ended.
1668 // TODO combine into 1
1669 private boolean isProcessEnded(String businessKey, String processName) {
1670 HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
1671 .processInstanceBusinessKey(businessKey).processDefinitionName(processName).singleResult();
1672 return processInstance != null && processInstance.getEndTime() != null;
1676 * Gets a variable value from a historical process instance. The business key must be unique.
1678 * @param businessKey the process business key
1679 * @param variableName the variable name
1680 * @return the variable value or null if the variable does not exist
1682 protected Object getVariableFromHistory(String businessKey, String variableName) {
1684 HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
1685 .processInstanceBusinessKey(businessKey).singleResult();
1687 if (processInstance == null) {
1691 HistoricVariableInstance v = historyService.createHistoricVariableInstanceQuery()
1692 .processInstanceId(processInstance.getId()).variableName(variableName).singleResult();
1693 return v == null ? null : v.getValue();
1694 } catch (Exception e) {
1695 logger.debug("Error retrieving variable {} from historical process with business key {}: ", variableName,
1702 * Gets a variable value from a process instance based on businessKey and process name. Must be used when multiple
1703 * instances exist with the same business key such as when business key is passed to subflows or shared across
1704 * multiple processes. This method can obtain variables from mainflows and from subflows.
1706 * @param businessKey the process business key
1707 * @param processName the process definition name
1708 * @param variableName the variable name
1709 * @return the variable value or null if the variable does not exist
1712 protected Object getVariableFromHistory(String businessKey, String processName, String variableName) {
1714 HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
1715 .processInstanceBusinessKey(businessKey).processDefinitionName(processName).singleResult();
1717 if (processInstance == null) {
1720 HistoricVariableInstance variable = historyService.createHistoricVariableInstanceQuery()
1721 .processInstanceId(processInstance.getId()).variableName(variableName).singleResult();
1723 return variable == null ? null : variable.getValue();
1724 } catch (ProcessEngineException e) {
1726 "Multiple proccess instances exist with process name {} and business key {}. Must pass instance "
1727 + "index as a parameter.",
1728 processName, businessKey);
1730 } catch (Exception e) {
1731 logger.debug("Error retrieving variable {} from historical process for process {} with business key {}: ",
1732 variableName, processName, businessKey, e);
1738 * Gets the value of a process variable from x instance of y process. Must be used when multiple instances exist
1739 * with the same business key AND process name. This method shall be used primarily for obtaining subflow variables
1740 * when the business key is passed to the subflow AND the subflow is called multiple times in a given flow.
1742 * @param businessKey the process business key
1743 * @param processName the name of the subflow that contains the variable
1744 * @param variableName the variable name
1745 * @param processInstanceIndex the instance in which the subprocess was called
1746 * @return the variable value or null if the variable does not exist
1749 protected Object getVariableFromHistory(String businessKey, int subflowInstanceIndex, String processName,
1750 String variableName) {
1752 List<HistoricProcessInstance> processInstanceList = historyService.createHistoricProcessInstanceQuery()
1753 .processInstanceBusinessKey(businessKey).processDefinitionName(processName).list();
1755 if (processInstanceList == null) {
1758 processInstanceList.sort((m1, m2) -> m1.getStartTime().compareTo(m2.getStartTime()));
1760 HistoricProcessInstance processInstance = processInstanceList.get(subflowInstanceIndex);
1761 HistoricVariableInstance variable = historyService.createHistoricVariableInstanceQuery()
1762 .processInstanceId(processInstance.getId()).variableName(variableName).singleResult();
1764 return variable == null ? null : variable.getValue();
1765 } catch (Exception e) {
1766 logger.debug("Error retrieving variable {} from historical process for process {} with business key {}: ",
1767 variableName, processName, businessKey, e);
1774 * Gets the value of a subflow variable from the specified subflow's historical process instance.
1776 * DEPRECATED - Use method getVariableFromHistory(businessKey, processName, variableName) instead
1778 * @param subflowName - the name of the subflow that contains the variable
1779 * @param variableName the variable name
1781 * @return the variable value, or null if the variable could not be obtained
1785 protected Object getVariableFromSubflowHistory(String subflowName, String variableName) {
1787 List<HistoricProcessInstance> processInstanceList =
1788 historyService.createHistoricProcessInstanceQuery().processDefinitionName(subflowName).list();
1790 if (processInstanceList == null) {
1794 processInstanceList.sort((m1, m2) -> m1.getStartTime().compareTo(m2.getStartTime()));
1796 HistoricProcessInstance processInstance = processInstanceList.get(0);
1798 HistoricVariableInstance v = historyService.createHistoricVariableInstanceQuery()
1799 .processInstanceId(processInstance.getId()).variableName(variableName).singleResult();
1800 return v == null ? null : v.getValue();
1801 } catch (Exception e) {
1802 logger.debug("Error retrieving variable {} from sub flow: {}, Exception is: ", variableName, subflowName,
1809 * Gets the value of a subflow variable from the subflow's historical process x instance.
1811 * DEPRECATED: Use method getVariableFromHistory(businessKey, processInstanceIndex, processName, variableName)
1814 * @param subflowName - the name of the subflow that contains the variable
1815 * @param variableName the variable name
1816 * @param subflowInstanceIndex - the instance of the subflow (use when same subflow is called more than once from
1819 * @return the variable value, or null if the variable could not be obtained
1822 protected Object getVariableFromSubflowHistory(int subflowInstanceIndex, String subflowName, String variableName) {
1824 List<HistoricProcessInstance> processInstanceList =
1825 historyService.createHistoricProcessInstanceQuery().processDefinitionName(subflowName).list();
1827 if (processInstanceList == null) {
1831 processInstanceList.sort((m1, m2) -> m1.getStartTime().compareTo(m2.getStartTime()));
1833 HistoricProcessInstance processInstance = processInstanceList.get(subflowInstanceIndex);
1835 HistoricVariableInstance v = historyService.createHistoricVariableInstanceQuery()
1836 .processInstanceId(processInstance.getId()).variableName(variableName).singleResult();
1837 return v == null ? null : v.getValue();
1838 } catch (Exception e) {
1839 logger.debug("Error retrieving variable {} from {} instance index of sub flow: {}, Exception is: ",
1840 variableName, subflowInstanceIndex, subflowName, e);
1846 * Extracts text from an XML element. This method is not namespace aware (namespaces are ignored). The first
1847 * matching element is selected.
1849 * @param xml the XML document or fragment
1850 * @param tag the desired element, e.g. "<name>"
1851 * @return the element text, or null if the element was not found
1853 protected String getXMLTextElement(String xml, String tag) {
1854 xml = removeXMLNamespaces(xml);
1856 if (!tag.startsWith("<")) {
1857 tag = "<" + tag + ">";
1860 int start = xml.indexOf(tag);
1866 int end = xml.indexOf('<', start + tag.length());
1872 return xml.substring(start + tag.length(), end);
1876 * Removes namespace definitions and prefixes from XML, if any.
1878 private String removeXMLNamespaces(String xml) {
1879 // remove xmlns declaration
1880 xml = xml.replaceAll("xmlns.*?(\"|\').*?(\"|\')", "");
1882 // remove opening tag prefix
1883 xml = xml.replaceAll("(<)(\\w+:)(.*?>)", "$1$3");
1885 // remove closing tags prefix
1886 xml = xml.replaceAll("(</)(\\w+:)(.*?>)", "$1$3");
1888 // remove extra spaces left when xmlns declarations are removed
1889 xml = xml.replaceAll("\\s+>", ">");
1895 * Asserts that two XML documents are semantically equivalent. Differences in whitespace or in namespace usage do
1896 * not affect the comparison.
1898 * @param expected the expected XML
1899 * @param actual the XML to test
1900 * @throws SAXException
1901 * @throws IOException
1903 public static void assertXMLEquals(String expected, String actual) throws SAXException, IOException {
1904 XMLUnit.setIgnoreWhitespace(true);
1905 XMLUnit.setIgnoreAttributeOrder(true);
1906 DetailedDiff diff = new DetailedDiff(XMLUnit.compareXML(expected, actual));
1907 List<?> allDifferences = diff.getAllDifferences();
1908 assertEquals("Differences found: " + diff.toString(), 0, allDifferences.size());
1912 * A test implementation of AsynchronousResponse.
1914 public class TestAsyncResponse {
1915 Response response = null;
1920 public synchronized void setResponse(Response response) {
1921 this.response = response;
1925 * Gets the response.
1927 * @return the response, or null if none has been produced yet
1929 public synchronized Response getResponse() {
1935 * An object that contains callback data for a "program".
1937 public class CallbackSet {
1938 private final Map<String, CallbackData> map = new HashMap<>();
1941 * Add untyped callback data to the set.
1943 * @param action the action with which the data is associated
1944 * @param content the callback data
1946 public void put(String action, String content) {
1947 map.put(action, new CallbackData(null, null, content));
1951 * Add callback data to the set.
1953 * @param action the action with which the data is associated
1954 * @param messageType the callback message type
1955 * @param content the callback data
1957 public void put(String action, String messageType, String content) {
1958 map.put(action, new CallbackData(null, messageType, content));
1962 * Add callback data to the set.
1964 * @param action the action with which the data is associated
1965 * @param contentType the callback HTTP content type
1966 * @param messageType the callback message type
1967 * @param content the callback data
1969 public void put(String action, String contentType, String messageType, String content) {
1970 map.put(action, new CallbackData(contentType, messageType, content));
1974 * Retrieve callback data from the set.
1976 * @param action the action with which the data is associated
1977 * @return the callback data, or null if there is none for the specified operation
1979 public CallbackData get(String action) {
1980 return map.get(action);
1985 * Represents a callback data item.
1987 public class CallbackData {
1988 private final String contentType;
1989 private final String messageType;
1990 private final String content;
1995 * @param contentType the HTTP content type (optional)
1996 * @param messageType the callback message type (optional)
1997 * @param content the content
1999 public CallbackData(String contentType, String messageType, String content) {
2000 this.contentType = contentType;
2001 this.messageType = messageType;
2002 this.content = content;
2006 * Gets the callback HTTP content type, possibly null.
2008 public String getContentType() {
2013 * Gets the callback message type, possibly null.
2015 public String getMessageType() {
2020 * Gets the callback content.
2022 public String getContent() {
2028 * A tool for evaluating XPath expressions.
2030 protected class XPathTool {
2031 private final DocumentBuilderFactory factory;
2032 private final SimpleNamespaceContext context = new SimpleNamespaceContext();
2033 private final XPath xpath = XPathFactory.newInstance().newXPath();
2034 private String xml = null;
2035 private Document doc = null;
2040 public XPathTool() {
2041 factory = DocumentBuilderFactory.newInstance();
2042 factory.setNamespaceAware(true);
2043 xpath.setNamespaceContext(context);
2049 * @param prefix the namespace prefix
2050 * @param uri the namespace uri
2052 public synchronized void addNamespace(String prefix, String uri) {
2053 context.add(prefix, uri);
2057 * Sets the XML content to be operated on.
2059 * @param xml the XML content
2061 public synchronized void setXML(String xml) {
2067 * Returns the document object.
2069 * @return the document object, or null if XML has not been set
2070 * @throws SAXException
2071 * @throws IOException
2072 * @throws ParserConfigurationException
2074 public synchronized Document getDocument() throws ParserConfigurationException, IOException, SAXException {
2084 * Evaluates the specified XPath expression and returns a string result. This method throws exceptions on error.
2086 * @param expression the expression
2087 * @return the result object
2088 * @throws ParserConfigurationException
2089 * @throws IOException
2090 * @throws SAXException
2091 * @throws XPathExpressionException on error
2093 public synchronized String evaluate(String expression)
2094 throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
2095 return (String) evaluate(expression, XPathConstants.STRING);
2099 * Evaluates the specified XPath expression. This method throws exceptions on error.
2101 * @param expression the expression
2102 * @param returnType the return type
2103 * @return the result object
2104 * @throws ParserConfigurationException
2105 * @throws IOException
2106 * @throws SAXException
2107 * @throws XPathExpressionException on error
2109 public synchronized Object evaluate(String expression, QName returnType)
2110 throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
2113 XPathExpression expr = xpath.compile(expression);
2114 return expr.evaluate(doc, returnType);
2118 * Private helper method that builds the document object. Assumes the calling method is synchronized.
2120 * @throws ParserConfigurationException
2121 * @throws IOException
2122 * @throws SAXException
2124 private void buildDocument() throws ParserConfigurationException, IOException, SAXException {
2127 throw new IOException("XML input is null");
2130 DocumentBuilder builder = factory.newDocumentBuilder();
2131 InputSource source = new InputSource(new StringReader(xml));
2132 doc = builder.parse(source);
2138 * A NamespaceContext class based on a Map.
2140 private class SimpleNamespaceContext implements NamespaceContext {
2141 private Map<String, String> prefixMap = new HashMap<>();
2142 private Map<String, String> uriMap = new HashMap<>();
2144 public synchronized void add(String prefix, String uri) {
2145 prefixMap.put(prefix, uri);
2146 uriMap.put(uri, prefix);
2150 public synchronized String getNamespaceURI(String prefix) {
2151 return prefixMap.get(prefix);
2155 public Iterator<String> getPrefixes(String uri) {
2156 List<String> list = new ArrayList<>();
2157 String prefix = uriMap.get(uri);
2158 if (prefix != null) {
2161 return list.iterator();
2165 public String getPrefix(String uri) {
2166 return uriMap.get(uri);
2171 * A VnfNotify XPathTool.
2173 protected class VnfNotifyXPathTool extends XPathTool {
2174 public VnfNotifyXPathTool() {
2175 addNamespace("tns", "http://org.onap.so/vnfNotify");
2180 * Helper class to make it easier to create this type.
2182 private static class CreateVnfNotificationOutputs extends CreateVnfNotification.Outputs {
2183 public void add(String key, String value) {
2184 Entry entry = new Entry();
2186 entry.setValue(value);
2187 getEntry().add(entry);
2192 * Helper class to make it easier to create this type.
2194 private static class UpdateVnfNotificationOutputs extends UpdateVnfNotification.Outputs {
2195 public void add(String key, String value) {
2196 Entry entry = new Entry();
2198 entry.setValue(value);
2199 getEntry().add(entry);