Merge "add in volume group model info lookup"
[so.git] / bpmn / mso-infrastructure-bpmn / src / test / java / org / onap / so / bpmn / common / WorkflowTest.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.so.bpmn.common;
22
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.fail;
25 import static org.onap.so.bpmn.core.json.JsonUtils.getJsonValue;
26 import static org.onap.so.bpmn.core.json.JsonUtils.updJsonValue;
27
28 import java.io.IOException;
29 import java.io.StringReader;
30 import java.lang.management.ManagementFactory;
31 import java.lang.management.RuntimeMXBean;
32 import java.util.ArrayList;
33 import java.util.HashMap;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.UUID;
38
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
52 import org.camunda.bpm.engine.HistoryService;
53 import org.camunda.bpm.engine.ProcessEngine;
54 import org.camunda.bpm.engine.ProcessEngineException;
55 import org.camunda.bpm.engine.RuntimeService;
56 import org.camunda.bpm.engine.history.HistoricProcessInstance;
57 import org.camunda.bpm.engine.history.HistoricVariableInstance;
58 import org.camunda.bpm.engine.runtime.ProcessInstance;
59 import org.camunda.bpm.engine.runtime.ProcessInstanceQuery;
60 import org.camunda.bpm.engine.test.ProcessEngineRule;
61 import org.camunda.bpm.engine.variable.impl.VariableMapImpl;
62 import org.custommonkey.xmlunit.DetailedDiff;
63 import org.custommonkey.xmlunit.XMLUnit;
64 import org.json.JSONArray;
65 import org.json.JSONObject;
66 import org.junit.Rule;
67 import org.onap.so.bpmn.common.adapter.sdnc.CallbackHeader;
68 import org.onap.so.bpmn.common.adapter.sdnc.SDNCAdapterCallbackRequest;
69 import org.onap.so.bpmn.common.adapter.sdnc.SDNCAdapterResponse;
70 import org.onap.so.bpmn.common.adapter.vnf.CreateVnfNotification;
71 import org.onap.so.bpmn.common.adapter.vnf.DeleteVnfNotification;
72 import org.onap.so.bpmn.common.adapter.vnf.MsoExceptionCategory;
73 import org.onap.so.bpmn.common.adapter.vnf.MsoRequest;
74 import org.onap.so.bpmn.common.adapter.vnf.UpdateVnfNotification;
75 import org.onap.so.bpmn.common.adapter.vnf.VnfRollback;
76 import org.onap.so.bpmn.common.workflow.context.WorkflowResponse;
77 import org.onap.so.bpmn.common.workflow.service.SDNCAdapterCallbackServiceImpl;
78 import org.onap.so.bpmn.common.workflow.service.VnfAdapterNotifyServiceImpl;
79 import org.onap.so.bpmn.common.workflow.service.WorkflowAsyncResource;
80 import org.onap.so.bpmn.common.workflow.service.WorkflowMessageResource;
81 import org.onap.so.bpmn.common.workflow.service.WorkflowResource;
82 import org.onap.so.bpmn.core.domain.Resource;
83 import org.onap.so.bpmn.core.domain.ServiceDecomposition;
84 import org.onap.so.logger.MsoLogger;
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;
92
93
94
95 /**
96  * A base class for Workflow tests.
97  * <p>
98  * WireMock response transformers may be specified by declaring public
99  * static fields with the @WorkflowTestTransformer annotation. For example:
100  * <pre>
101  *     @WorkflowTestTransformer
102  *     public static final ResponseTransformer sdncAdapterMockTransformer =
103  *         new SDNCAdapterMockTransformer();
104  * </pre>
105  */
106
107 public abstract class WorkflowTest {
108
109         private static final MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL, WorkflowTest.class);
110
111         //TODO this is not used anymore, can maybe be removed
112         @Rule
113         public ProcessEngineRule processEngineRule;
114
115         @Autowired
116         protected WorkflowResource workflowResourceSync;
117
118         @Autowired
119         protected ProcessEngine processEngine;
120
121         @Autowired
122         protected RuntimeService runtimeService;
123
124         @Autowired
125         protected HistoryService historyService;
126
127         @Autowired
128         private WorkflowAsyncResource workflowResource;
129
130         @Autowired
131         private WorkflowMessageResource workflowMessageResource;
132
133         @Autowired
134         SDNCAdapterCallbackServiceImpl callbackService;
135         /**
136          * Content-Type for XML.
137          */
138         protected static final String XML = "application/xml";
139
140         /**
141          * Content-Type for JSON.
142          */
143         protected static final String JSON = "application/json; charset=UTF-8";
144
145         private static final int timeout = 2000;
146         
147         /**
148          * Constructor.
149          */
150         public WorkflowTest() throws RuntimeException {
151         }
152
153         /**
154          * The current request ID.  Normally set when an "invoke" method is called.
155          */
156         protected volatile String msoRequestId = null;
157
158         /**
159          * The current service instance ID.  Normally set when an "invoke" method
160          * is called.
161          */
162         protected volatile String msoServiceInstanceId = null;
163
164         /**
165          * Logs a test start method.
166          */
167         protected void logStart() {
168                 msoLogger.debug("STARTED TEST");
169         }
170
171         /**
172          * Logs a test end method.
173          */
174         protected void logEnd() {
175                 msoLogger.debug("ENDED TEST");
176         }
177
178         /**
179          * Invokes a subprocess.
180          * @param processKey the process key
181          * @param businessKey a unique key that will identify the process instance
182          * @param injectedVariables variables to inject into the process
183          */
184         protected void invokeSubProcess(String processKey, String businessKey, Map<String, Object> injectedVariables) {
185                 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
186                 List<String> arguments = runtimeMxBean.getInputArguments();
187                 msoLogger.debug("JVM args = " + arguments);
188
189                 msoRequestId = (String) injectedVariables.get("mso-request-id");
190                 String requestId = (String) injectedVariables.get("msoRequestId");
191
192                 if (msoRequestId == null && requestId == null) {
193                         String msg = "mso-request-id variable was not provided";
194                         msoLogger.debug(msg);
195                         fail(msg);
196                 }
197
198                 // Note: some scenarios don't have a service-instance-id, may be null
199                 msoServiceInstanceId = (String) injectedVariables.get("mso-service-instance-id");
200
201
202                 runtimeService.startProcessInstanceByKey(processKey, businessKey, injectedVariables);
203         }
204
205         protected String invokeSubProcess(String processKey,  Map<String, Object> injectedVariables) {
206                 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
207                 List<String> arguments = runtimeMxBean.getInputArguments();
208                 msoLogger.debug("JVM args = " + arguments);
209
210                 msoRequestId = (String) injectedVariables.get("mso-request-id");
211                 String requestId = (String) injectedVariables.get("msoRequestId");
212
213                 if (msoRequestId == null && requestId == null) {
214                         String msg = "mso-request-id variable was not provided";
215                         msoLogger.debug(msg);
216                         fail(msg);
217                 }
218
219                 // Note: some scenarios don't have a service-instance-id, may be null
220                 msoServiceInstanceId = (String) injectedVariables.get("mso-service-instance-id");
221
222
223                 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processKey, msoRequestId, injectedVariables);
224                 return processInstance.getId();
225         }
226
227         /**
228          * Invokes an asynchronous process.
229          * 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
236          */
237         protected TestAsyncResponse invokeAsyncProcess(String processKey,
238                         String schemaVersion, String businessKey, String request) throws InterruptedException {
239                 return invokeAsyncProcess(processKey, schemaVersion, businessKey, request, null);
240         }
241
242         /**
243          * Invokes an asynchronous process.
244          * 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
252          */
253         protected TestAsyncResponse invokeAsyncProcess(String processKey,
254                         String schemaVersion, String businessKey, String request,
255                         Map<String, Object> injectedVariables) {
256
257                 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
258                 List<String> arguments = runtimeMxBean.getInputArguments();
259                 msoLogger.debug("JVM args = " + arguments);
260
261                 Map<String, Object> variables = createVariables(schemaVersion, businessKey,
262                         request, injectedVariables, false);
263                 VariableMapImpl variableMapImpl = createVariableMapImpl(variables);
264
265                 msoLogger.debug("Sending " + request + " to " + processKey + " process");
266
267                 TestAsyncResponse asyncResponse = new TestAsyncResponse();
268
269                 asyncResponse.setResponse(workflowResource.startProcessInstanceByKey( processKey, variableMapImpl));
270
271                 return asyncResponse;
272         }
273
274         /**
275          * Invokes an asynchronous process.
276          * Errors are handled with junit assertions and will cause the test to fail.
277          * @param processKey the process key
278          * @param schemaVersion the API schema version, e.g. "v1"
279          * @param businessKey a unique key that will identify the process instance
280          * @param request the request
281          * @param injectedVariables optional variables to inject into the process
282          * @param serviceInstantiationModel indicates whether this method is being
283          * invoked for a flow that is designed using the service instantiation model
284          * @return a TestAsyncResponse object associated with the test
285          * @throws InterruptedException
286          */
287         protected Response invokeAsyncProcess(String processKey,
288                         String schemaVersion, String businessKey, String request,
289                         Map<String, Object> injectedVariables, boolean serviceInstantiationModel) {
290
291                 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
292                 List<String> arguments = runtimeMxBean.getInputArguments();
293                 msoLogger.debug("JVM args = " + arguments);
294
295                 Map<String, Object> variables = createVariables(schemaVersion, businessKey,
296                         request, injectedVariables, serviceInstantiationModel);
297                 VariableMapImpl variableMapImpl = createVariableMapImpl(variables);
298
299                 msoLogger.debug("Sending " + request + " to " + processKey + " process");
300
301                 return workflowResource.startProcessInstanceByKey( processKey, variableMapImpl);
302
303         }
304
305         /**
306          * Private helper method that creates a variable map for a request.
307          * Errors are handled with junit assertions and will cause the test to fail.
308          * @param schemaVersion the API schema version, e.g. "v1"
309          * @param businessKey a unique key that will identify the process instance
310          * @param request the request
311          * @param injectedVariables optional variables to inject into the process
312          * @param serviceInstantiationModel indicates whether this method is being
313          * invoked for a flow that is designed using the service instantiation model
314          * @return a variable map
315          */
316         private Map<String, Object> createVariables(String schemaVersion,
317                         String businessKey, String request, Map<String, Object> injectedVariables,
318                         boolean serviceInstantiationModel) {
319
320                 Map<String, Object> variables = new HashMap<>();
321
322                 // These variables may be overridded by injected variables.
323                 variables.put("mso-service-request-timeout", "180");
324                 variables.put("isDebugLogEnabled", "true");
325
326                 // These variables may not be overridded by injected variables.
327                 String[] notAllowed = new String[] {
328                                 "mso-schema-version",
329                                 "mso-business-key",
330                                 "bpmnRequest",
331                                 "mso-request-id",
332                                 "mso-service-instance-id"
333                 };
334
335                 if (injectedVariables != null) {
336                         for (String key : injectedVariables.keySet()) {
337                                 for (String var : notAllowed) {
338                                         if (var.equals(key)) {
339                                                 String msg = "Cannot specify " + var + " in injected variables";
340                                                 msoLogger.debug(msg);
341                                                 fail(msg);
342                                         }
343                                 }
344
345                                 variables.put(key, injectedVariables.get(key));
346                         }
347                 }
348
349                 variables.put("mso-schema-version", schemaVersion);
350                 variables.put("mso-business-key", businessKey);
351                 variables.put("bpmnRequest", request);
352
353                 if (serviceInstantiationModel) {
354
355                         /*
356                          * The request ID and the service instance ID are generated for flows
357                          * that follow the service instantiation model unless "requestId" and
358                          * "serviceInstanceId" are injected variables.
359                          */
360
361                         try {
362                                 msoRequestId = (String) injectedVariables.get("requestId");
363                                 variables.put("mso-request-id", msoRequestId);
364                                 msoServiceInstanceId = (String) injectedVariables.get("serviceInstanceId");
365                                 variables.put("mso-service-instance-id", msoServiceInstanceId);
366                         }
367                         catch(Exception e) {
368                         }
369                         if (msoRequestId == null || msoRequestId.trim().equals("")) {
370                                 msoLogger.debug("No requestId element in injectedVariables");
371                                 variables.put("mso-request-id", UUID.randomUUID().toString());
372                         }
373                         if (msoServiceInstanceId == null || msoServiceInstanceId.trim().equals("")) {
374                                 msoLogger.debug("No seviceInstanceId element in injectedVariables");
375                                 variables.put("mso-service-instance-id", UUID.randomUUID().toString());
376                         }
377
378                 } else {
379                         msoRequestId = getXMLTextElement(request, "request-id");
380
381                         if (msoRequestId == null) {
382                                 //check in injected variables
383                                 try {
384                                         msoRequestId = (String) injectedVariables.get("requestId");
385                                 }
386                                 catch(Exception e) {
387                                 }
388                                 if (msoRequestId == null || msoRequestId.trim().equals("")) {
389                                         String msg = "No request-id element in " + request;
390                                         msoLogger.debug(msg);
391                                         fail(msg);
392                                 }
393                         }
394
395                         variables.put("mso-request-id", msoRequestId);
396
397                         // Note: some request types don't have a service-instance-id
398                         msoServiceInstanceId = getXMLTextElement(request, "service-instance-id");
399
400                         if (msoServiceInstanceId != null) {
401                                 variables.put("mso-service-instance-id", msoServiceInstanceId);
402                         }
403                 }
404
405                 return variables;
406         }
407
408         /**
409          * Private helper method that creates a camunda VariableMapImpl from a simple
410          * variable map.
411          * @param variables the simple variable map
412          * @return a VariableMap
413          */
414         private VariableMapImpl createVariableMapImpl(Map<String, Object> variables) {
415                 Map<String, Object> wrappedVariables = new HashMap<>();
416
417                 for (String key : variables.keySet()) {
418                         Object value = variables.get(key);
419                         wrappedVariables.put(key, wrapVariableValue(value));
420                 }
421
422                 VariableMapImpl variableMapImpl = new VariableMapImpl();
423                 variableMapImpl.put("variables", wrappedVariables);
424                 return variableMapImpl;
425         }
426
427         /**
428          * Private helper method that wraps a variable value for inclusion in a
429          * camunda VariableMapImpl.
430          * @param value the variable value
431          * @return the wrapped variable
432          */
433         private Map<String, Object> wrapVariableValue(Object value) {
434                 HashMap<String, Object> valueMap = new HashMap<>();
435                 valueMap.put("value", value);
436                 return valueMap;
437         }
438
439         /**
440          * Receives a response from an asynchronous process.
441          * Errors are handled with junit assertions and will cause the test to fail.
442          * @param businessKey the process business key
443          * @param asyncResponse the TestAsyncResponse object associated with the test
444          * @param timeout the timeout in milliseconds
445          * @return the WorkflowResponse
446          */
447         protected WorkflowResponse receiveResponse(String businessKey,
448                         TestAsyncResponse asyncResponse, long timeout) {
449                 msoLogger.debug("Waiting " + timeout + "ms for process with business key " + businessKey
450                         + " to send a response");
451
452                 long now = System.currentTimeMillis() + timeout;
453                 long endTime = now + timeout;
454
455                 while (now <= endTime) {
456                         Response response = asyncResponse.getResponse();
457
458                         if (response != null) {
459                                 msoLogger.debug("Received a response from process with business key " + businessKey);
460
461                                 Object entity = response.getEntity();
462
463                                 if (!(entity instanceof WorkflowResponse)) {
464                                         String msg = "Response entity is " +
465                                                 (entity == null ? "null" : entity.getClass().getName()) +
466                                                 ", expected WorkflowResponse";
467                                         msoLogger.debug(msg);
468                                         fail(msg);
469                                         return null; // unreachable
470                                 }
471
472                                 return (WorkflowResponse) entity;
473                         }
474
475                         try {
476                                 Thread.sleep(200);
477                         } catch (InterruptedException e) {
478                                 String msg = "Interrupted waiting for a response from process with business key " +
479                                         businessKey;
480                                 msoLogger.debug(msg);
481                                 fail(msg);
482                                 return null; // unreachable
483                         }
484
485                         now = System.currentTimeMillis();
486                 }
487
488                 String msg = "No response received from process with business key " + businessKey +
489                         " within " + timeout + "ms";
490                 msoLogger.debug(msg);
491                 fail("Process with business key " + businessKey + " did not end within 10000ms");
492                 return null; // unreachable
493         }
494
495         /**
496          * Runs a program to inject SDNC callback data into the test environment.
497          * A program is essentially just a list of keys that identify callback data
498          * to be injected, in sequence. An example program:
499          * <pre>
500          *     reserve, assign, delete:ERR
501          * </pre>
502          * Errors are handled with junit assertions and will cause the test to fail.
503          * @param callbacks an object containing callback data for the program
504          * @param program the program to execute
505          */
506         protected void injectSDNCRestCallbacks(CallbackSet callbacks, String program) {
507
508                 String[] cmds = program.replaceAll("\\s+", "").split(",");
509
510                 for (String cmd : cmds) {
511                         String action = cmd;
512                         String modifier = "STD";
513
514                         if (cmd.contains(":")) {
515                                 String[] parts = cmd.split(":");
516                                 action = parts[0];
517                                 modifier = parts[1];
518                         }
519
520                         String content = null;
521                         String contentType = null;
522
523                         if ("STD".equals(modifier)) {
524                                 CallbackData callbackData = callbacks.get(action);
525
526                                 if (callbackData == null) {
527                                         String msg = "No callback defined for '" + action + "' SDNC request";
528                                         msoLogger.debug(msg);
529                                         fail(msg);
530                                 }
531
532                                 content = callbackData.getContent();
533                                 contentType = callbackData.getContentType();
534                         } else if ("ERR".equals(modifier)) {
535                                 content = "{\"SDNCServiceError\":{\"sdncRequestId\":\"((REQUEST-ID))\",\"responseCode\":\"500\",\"responseMessage\":\"SIMULATED ERROR FROM SDNC ADAPTER\",\"ackFinalIndicator\":\"Y\"}}";
536                                 contentType = JSON;
537                         } else {
538                                 String msg = "Invalid SDNC program modifier: '" + modifier + "'";
539                                 msoLogger.debug(msg);
540                                 fail(msg);
541                         }
542
543                         if (contentType == null) {
544                                 // Default for backward compatibility with existing tests.
545                                 contentType = JSON;
546                         }
547
548                         if (!injectSDNCRestCallback(contentType, content, 10000)) {
549                                 fail("Failed to inject SDNC '" + action + "' callback");
550                         }
551
552                         try {
553                                 Thread.sleep(1000);
554                         } catch (InterruptedException e) {
555                                 fail("Interrupted after injection of SDNC '" + action + "' callback");
556                         }
557                 }
558         }
559
560         /**
561          * Runs a program to inject SDNC events into the test environment.
562          * A program is essentially just a list of keys that identify event data
563          * to be injected, in sequence. An example program:
564          * <pre>
565          *     event1, event2
566          * </pre>
567          * NOTE: Each callback must have a message type associated with it, e.g.
568          * "SDNCAEvent".
569          * Errors are handled with junit assertions and will cause the test to fail.
570          * @param callbacks an object containing event data for the program
571          * @param program the program to execute
572          */
573         protected void injectSDNCEvents(CallbackSet callbacks, String program) {
574                 injectWorkflowMessages(callbacks, program);
575         }
576
577         /**
578          * Runs a program to inject SDNC callback data into the test environment.
579          * A program is essentially just a list of keys that identify callback data
580          * to be injected, in sequence. An example program:
581          * <pre>
582          *     reserve, assign, delete:ERR
583          * </pre>
584          * Errors are handled with junit assertions and will cause the test to fail.
585          * Uses the static/default timeout value for backward compatibility.
586          * @param callbacks an object containing callback data for the program
587          * @param program the program to execute
588          */
589         protected void injectSDNCCallbacks(CallbackSet callbacks, String program) {
590                 injectSDNCCallbacks(callbacks, program, timeout);
591         }
592
593         /**
594          * Runs a program to inject SDNC callback data into the test environment.
595          * A program is essentially just a list of keys that identify callback data
596          * to be injected, in sequence. An example program:
597          * <pre>
598          *     reserve, assign, delete:ERR
599          * </pre>
600          * Errors are handled with junit assertions and will cause the test to fail.
601          * @param callbacks an object containing callback data for the program
602          * @param program the program to execute
603          * @param timeout a timeout value to wait for the callback
604          */
605         protected void injectSDNCCallbacks(CallbackSet callbacks, String program, int timeout) {
606
607                 String[] cmds = program.replaceAll("\\s+", "").split(",");
608
609                 for (String cmd : cmds) {
610                         String action = cmd;
611                         String modifier = "STD";
612
613                         if (cmd.contains(":")) {
614                                 String[] parts = cmd.split(":");
615                                 action = parts[0];
616                                 modifier = parts[1];
617                         }
618
619                         String content = null;
620                         int respCode = 200;
621                         String respMsg = "OK";
622
623                         if ("STD".equals(modifier)) {
624                                 CallbackData callbackData = callbacks.get(action);
625
626                                 if (callbackData == null) {
627                                         String msg = "No callback defined for '" + action + "' SDNC request";
628                                         msoLogger.debug(msg);
629                                         fail(msg);
630                                 }
631
632                                 content = callbackData.getContent();
633                                 respCode = 200;
634                                 respMsg = "OK";
635                         } else if ("CREATED".equals(modifier)) {
636                                 CallbackData callbackData = callbacks.get(action);
637
638                                 if (callbackData == null) {
639                                         String msg = "No callback defined for '" + action + "' SDNC request";
640                                         msoLogger.debug(msg);
641                                         fail(msg);
642                                 }
643
644                                 content = callbackData.getContent();
645                                 respCode = 201;
646                                 respMsg = "Created";
647                         } else if ("ERR".equals(modifier)) {
648                                 content = "<svc-request-id>((REQUEST-ID))</svc-request-id><response-code>500</response-code><response-message>SIMULATED ERROR FROM SDNC ADAPTER</response-message>";
649                                 respCode = 500;
650                                 respMsg = "SERVER ERROR";
651                         } else {
652                                 String msg = "Invalid SDNC program modifier: '" + modifier + "'";
653                                 msoLogger.debug(msg);
654                                 fail(msg);
655                         }
656
657                         if (!injectSDNCCallback(respCode, respMsg, content, 10000)) {
658                                 fail("Failed to inject SDNC '" + action + "' callback");
659                         }
660
661                         try {
662                                 Thread.sleep(1000);
663                         } catch (InterruptedException e) {
664                                 fail("Interrupted after injection of SDNC '" + action + "' callback");
665                         }
666                 }
667         }
668
669         /**
670          * Runs a program to inject VNF adapter REST callback data into the test environment.
671          * A program is essentially just a list of keys that identify callback data
672          * to be injected, in sequence. An example program:
673          * <pre>
674          *     create, rollback
675          * </pre>
676          * Errors are handled with junit assertions and will cause the test to fail.
677          * @param callbacks an object containing callback data for the program
678          * @param program the program to execute
679          */
680         protected void injectVNFRestCallbacks(CallbackSet callbacks, String program) {
681
682                 String[] cmds = program.replaceAll("\\s+", "").split(",");
683
684                 for (String cmd : cmds) {
685                         String action = cmd;
686                         String modifier = "STD";
687
688                         if (cmd.contains(":")) {
689                                 String[] parts = cmd.split(":");
690                                 action = parts[0];
691                                 modifier = parts[1];
692                         }
693
694                         String content = null;
695                         String contentType = null;
696
697                         if ("STD".equals(modifier)) {
698                                 CallbackData callbackData = callbacks.get(action);
699
700                                 if (callbackData == null) {
701                                         String msg = "No callback defined for '" + action + "' VNF REST request";
702                                         msoLogger.debug(msg);
703                                         fail(msg);
704                                 }
705
706                                 content = callbackData.getContent();
707                                 contentType = callbackData.getContentType();
708                         } else if ("ERR".equals(modifier)) {
709                                 content = "SIMULATED ERROR FROM VNF ADAPTER";
710                                 contentType = "text/plain";
711                         } else {
712                                 String msg = "Invalid VNF REST program modifier: '" + modifier + "'";
713                                 msoLogger.debug(msg);
714                                 fail(msg);
715                         }
716
717                         if (contentType == null) {
718                                 // Default for backward compatibility with existing tests.
719                                 contentType = XML;
720                         }
721
722                         if (!injectVnfAdapterRestCallback(contentType, content, 10000)) {
723                                 fail("Failed to inject VNF REST '" + action + "' callback");
724                         }
725
726                         try {
727                                 Thread.sleep(1000);
728                         } catch (InterruptedException e) {
729                                 fail("Interrupted after injection of VNF REST '" + action + "' callback");
730                         }
731                 }
732         }
733
734         /**
735          * Runs a program to inject VNF callback data into the test environment.
736          * A program is essentially just a list of keys that identify callback data
737          * to be injected, in sequence. An example program:
738          * <pre>
739          *     createVnf, deleteVnf
740          * </pre>
741          * Errors are handled with junit assertions and will cause the test to fail.
742          * @param callbacks an object containing callback data for the program
743          * @param program the program to execute
744          */
745         protected void injectVNFCallbacks(CallbackSet callbacks, String program) {
746
747                 String[] cmds = program.replaceAll("\\s+", "").split(",");
748
749                 for (String cmd : cmds) {
750                         String action = cmd;
751                         String modifier = "STD";
752
753                         if (cmd.contains(":")) {
754                                 String[] parts = cmd.split(":");
755                                 action = parts[0];
756                                 modifier = parts[1];
757                         }
758
759                         String content = null;
760
761                         if ("STD".equals(modifier)) {
762                                 CallbackData callbackData = callbacks.get(action);
763
764                                 if (callbackData == null) {
765                                         String msg = "No callback defined for '" + action + "' VNF request";
766                                         msoLogger.debug(msg);
767                                         fail(msg);
768                                 }
769
770                                 content = callbackData.getContent();
771                         } else if ("ERR".equals(modifier)) {
772                                 String msg = "Currently unsupported VNF program modifier: '" + modifier + "'";
773                                 msoLogger.debug(msg);
774                                 fail(msg);
775                         } else {
776                                 String msg = "Invalid VNF program modifier: '" + modifier + "'";
777                                 msoLogger.debug(msg);
778                                 fail(msg);
779                         }
780
781                         boolean injected = false;
782
783                         if (content.contains("createVnfNotification")) {
784                                 injected = injectCreateVNFCallback(content, 10000);
785                         } else if (content.contains("deleteVnfNotification")) {
786                                 injected = injectDeleteVNFCallback(content, 10000);
787                         } else if (content.contains("updateVnfNotification")) {
788                                 injected = injectUpdateVNFCallback(content, 10000);
789                         }
790
791                         if (!injected) {
792                                 String msg = "Failed to inject VNF '" + action + "' callback";
793                                 msoLogger.debug(msg);
794                                 fail(msg);
795                         }
796
797                         try {
798                                 Thread.sleep(1000);
799                         } catch (InterruptedException e) {
800                                 fail("Interrupted after injection of VNF '" + action + "' callback");
801                         }
802                 }
803         }
804
805         /**
806          * Waits for the number of running processes with the specified process
807          * definition key to equal a particular count.
808          * @param processKey the process definition key
809          * @param count the desired count
810          * @param timeout the timeout in milliseconds
811          */
812         protected void waitForRunningProcessCount(String processKey, int count, long timeout) {
813                 msoLogger.debug("Waiting " + timeout + "ms for there to be " + count + " "
814                         + processKey + " instances");
815
816                 long now = System.currentTimeMillis() + timeout;
817                 long endTime = now + timeout;
818                 int last = -1;
819
820                 while (now <= endTime) {
821                         int actual = runtimeService
822                                 .createProcessInstanceQuery()
823                                 .processDefinitionKey(processKey)
824                                 .list().size();
825
826                         if (actual != last) {
827                                 msoLogger.debug("There are now " + actual + " "
828                                         + processKey + " instances");
829                                 last = actual;
830                         }
831
832                         if (actual == count) {
833                                 return;
834                         }
835
836                         try {
837                                 Thread.sleep(200);
838                         } catch (InterruptedException e) {
839                                 String msg = "Interrupted waiting for there to be " + count + " "
840                                         + processKey + " instances";
841                                 msoLogger.debug(msg);
842                                 fail(msg);
843                         }
844
845                         now = System.currentTimeMillis();
846                 }
847
848                 String msg = "Timed out waiting for there to be " + count + " "
849                         + processKey + " instances";
850                 msoLogger.debug(msg);
851                 fail(msg);
852         }
853
854         /**
855          * Waits for the specified process variable to be set.
856          * @param processKey the process definition key
857          * @param variable the variable name
858          * @param timeout the timeout in milliseconds
859          * @return the variable value, or null if it cannot be obtained
860          *         in the specified time
861          */
862         protected Object getProcessVariable(String processKey, String variable,
863                         long timeout) {
864
865                 msoLogger.debug("Waiting " + timeout + "ms for "
866                         + processKey + "." + variable + " to be set");
867
868                 long now = System.currentTimeMillis() + timeout;
869                 long endTime = now + timeout;
870
871                 ProcessInstance processInstance = null;
872                 Object value = null;
873
874                 while (value == null) {
875                         if (now > endTime) {
876                                 if (processInstance == null) {
877                                         msoLogger.debug("Timed out waiting for "
878                                                 + processKey + " to start");
879                                 } else {
880                                         msoLogger.debug("Timed out waiting for "
881                                                 + processKey + "[" + processInstance.getId()
882                                                 + "]." + variable + " to be set");
883                                 }
884
885                                 return null;
886                         }
887
888                         ProcessInstanceQuery processInstanceQuery = null;
889                         if (processInstance == null) {
890                                 processInstanceQuery = runtimeService
891                                         .createProcessInstanceQuery()
892                                         .processDefinitionKey(processKey);
893                         }
894
895                         if(processInstanceQuery.count() <= 1){
896                                 processInstance = processInstanceQuery.singleResult();
897                         }else{
898                                 //TODO There shouldnt be more than one in the list but seems to be happening, need to figure out why happening and best way to get correct one from list
899                                 msoLogger.debug("Process Instance Query returned " + processInstanceQuery.count() + " instance. Getting the last instance in the list");
900                                 List<ProcessInstance> processList = processInstanceQuery.list();
901                                 processInstance = processList.get((processList.size() - 1));
902                         }
903
904                         if (processInstance != null) {
905                                 value = runtimeService
906                                         .getVariable(processInstance.getId(), variable);
907                         }
908
909                         try {
910                                 Thread.sleep(200);
911                         } catch (InterruptedException e) {
912                                 msoLogger.debug("Interrupted waiting for "
913                                         + processKey + "." + variable + " to be set");
914                                 return null;
915                         }
916
917                         now = System.currentTimeMillis();
918                 }
919
920                 msoLogger.debug(processKey + "["
921                         + processInstance.getId() + "]." + variable + "="
922                         + value);
923
924                 return value;
925         }
926
927         /**
928          * Injects a single SDNC adapter callback request. The specified callback data
929          * may contain the placeholder string ((REQUEST-ID)) which is replaced with
930          * the actual SDNC request ID. Note: this is not the requestId in the original
931          * MSO request.
932          * @param contentType the HTTP content type for the callback
933          * @param content the content of the callback
934          * @param timeout the timeout in milliseconds
935          * @return true if the callback could be injected, false otherwise
936          */
937         protected boolean injectSDNCRestCallback(String contentType, String content, long timeout) {
938                 String sdncRequestId = (String) getProcessVariable("SDNCAdapterRestV1",
939                         "SDNCAResponse_CORRELATOR", timeout);
940
941                 if (sdncRequestId == null) {
942                         sdncRequestId = (String) getProcessVariable("SDNCAdapterRestV2",
943                                 "SDNCAResponse_CORRELATOR", timeout);
944                 }
945
946                 if (sdncRequestId == null) {
947                         return false;
948                 }
949
950                 content = content.replace("((REQUEST-ID))", sdncRequestId);
951                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
952                 content = content.replace("{{REQUEST-ID}}", sdncRequestId);
953
954                 msoLogger.debug("Injecting SDNC adapter callback");
955
956                 Response response = workflowMessageResource.deliver(contentType, "SDNCAResponse", sdncRequestId, content);
957                 msoLogger.debug("Workflow response to SDNC adapter callback: " + response);
958                 return true;
959         }
960
961         /**
962          * Injects a single SDNC adapter callback request. The specified callback data
963          * may contain the placeholder string ((REQUEST-ID)) which is replaced with
964          * the actual SDNC request ID. Note: this is not the requestId in the original
965          * MSO request.
966          * @param content the content of the callback
967          * @param respCode the response code (normally 200)
968          * @param respMsg the response message (normally "OK")
969          * @param timeout the timeout in milliseconds
970          * @return true if the callback could be injected, false otherwise
971          */
972         protected boolean injectSDNCCallback(int respCode, String respMsg,
973                         String content, long timeout) {
974
975                 String sdncRequestId = (String) getProcessVariable("sdncAdapter",
976                         "SDNCA_requestId", timeout);
977
978                 if (sdncRequestId == null) {
979                         return false;
980                 }
981
982                 content = content.replace("((REQUEST-ID))", sdncRequestId);
983                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
984                 content = content.replace("{{REQUEST-ID}}", sdncRequestId);
985
986                 // TODO this needs to be fixed. It is causing double tags and content
987                 // Need to parse content before setting below since content includes not just RequestData or modify callback files to only contain RequestData contents.
988
989                 msoLogger.debug("Injecting SDNC adapter callback");
990                 CallbackHeader callbackHeader = new CallbackHeader();
991                 callbackHeader.setRequestId(sdncRequestId);
992                 callbackHeader.setResponseCode(String.valueOf(respCode));
993                 callbackHeader.setResponseMessage(respMsg);
994                 SDNCAdapterCallbackRequest sdncAdapterCallbackRequest = new SDNCAdapterCallbackRequest();
995                 sdncAdapterCallbackRequest.setCallbackHeader(callbackHeader);
996                 sdncAdapterCallbackRequest.setRequestData(content);
997                 SDNCAdapterResponse sdncAdapterResponse = callbackService.sdncAdapterCallback(sdncAdapterCallbackRequest);
998                 msoLogger.debug("Workflow response to SDNC adapter callback: " + sdncAdapterResponse);
999
1000                 return true;
1001         }
1002
1003         /**
1004          * Injects a single VNF adapter callback request. The specified callback data
1005          * may contain the placeholder string ((MESSAGE-ID)) which is replaced with
1006          * the actual message ID. Note: this is not the requestId in the original
1007          * MSO request.
1008          * @param contentType the HTTP content type for the callback
1009          * @param content the content of the callback
1010          * @param timeout the timeout in milliseconds
1011          * @return true if the callback could be injected, false otherwise
1012          */
1013         protected boolean injectVnfAdapterRestCallback(String contentType, String content, long timeout) {
1014                 String messageId = (String) getProcessVariable("vnfAdapterRestV1",
1015                         "VNFAResponse_CORRELATOR", timeout);
1016
1017                 if (messageId == null) {
1018                         return false;
1019                 }
1020
1021                 content = content.replace("((MESSAGE-ID))", messageId);
1022                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
1023                 content = content.replace("{{MESSAGE-ID}}", messageId);
1024
1025                 msoLogger.debug("Injecting VNF adapter callback");
1026
1027                 Response response = workflowMessageResource.deliver(contentType, "VNFAResponse", messageId, content);
1028                 msoLogger.debug("Workflow response to VNF adapter callback: " + response);
1029                 return true;
1030         }
1031
1032         /**
1033          * Injects a Create VNF adapter callback request. The specified callback data
1034          * may contain the placeholder string ((MESSAGE-ID)) which is replaced with
1035          * the actual message ID.  It may also contain the placeholder string
1036          * ((REQUEST-ID)) which is replaced request ID of the original MSO request.
1037          * @param content the content of the callback
1038          * @param timeout the timeout in milliseconds
1039          * @return true if the callback could be injected, false otherwise
1040          * @throws JAXBException if the content does not adhere to the schema
1041          */
1042         protected boolean injectCreateVNFCallback(String content, long timeout) {
1043
1044                 String messageId = (String) getProcessVariable("vnfAdapterCreateV1",
1045                         "VNFC_messageId", timeout);
1046
1047                 if (messageId == null) {
1048                         return false;
1049                 }
1050
1051                 content = content.replace("((MESSAGE-ID))", messageId);
1052                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
1053                 content = content.replace("{{MESSAGE-ID}}", messageId);
1054
1055                 if(content.contains("((REQUEST-ID))")){
1056                         content = content.replace("((REQUEST-ID))", msoRequestId);
1057                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
1058                         content = content.replace("{{REQUEST-ID}}", msoRequestId);
1059                 }
1060
1061                 msoLogger.debug("Injecting VNF adapter callback");
1062
1063                 // Is it possible to unmarshal this with JAXB?  I couldn't.
1064
1065                 CreateVnfNotification createVnfNotification = new CreateVnfNotification();
1066                 XPathTool xpathTool = new VnfNotifyXPathTool();
1067                 xpathTool.setXML(content);
1068
1069                 try {
1070                         String completed = xpathTool.evaluate(
1071                                 "/tns:createVnfNotification/tns:completed/text()");
1072                         createVnfNotification.setCompleted("true".equals(completed));
1073
1074                         String vnfId = xpathTool.evaluate(
1075                                 "/tns:createVnfNotification/tns:vnfId/text()");
1076                         createVnfNotification.setVnfId(vnfId);
1077
1078                         NodeList entries = (NodeList) xpathTool.evaluate(
1079                                 "/tns:createVnfNotification/tns:outputs/tns:entry",
1080                                 XPathConstants.NODESET);
1081
1082                         CreateVnfNotificationOutputs outputs = new CreateVnfNotificationOutputs();
1083
1084                         for (int i = 0; i < entries.getLength(); i++) {
1085                                 Node node = entries.item(i);
1086
1087                                 if (node.getNodeType() == Node.ELEMENT_NODE) {
1088                                         Element entry = (Element) node;
1089                                         String key = entry.getElementsByTagNameNS("*", "key").item(0).getTextContent();
1090                                         String value = entry.getElementsByTagNameNS("*", "value").item(0).getTextContent();
1091                                         outputs.add(key, value);
1092                                 }
1093                         }
1094
1095                         createVnfNotification.setOutputs(outputs);
1096
1097                         VnfRollback rollback = new VnfRollback();
1098
1099                         String cloudSiteId = xpathTool.evaluate(
1100                                 "/tns:createVnfNotification/tns:rollback/tns:cloudSiteId/text()");
1101                         rollback.setCloudSiteId(cloudSiteId);
1102
1103                         String requestId = xpathTool.evaluate(
1104                                 "/tns:createVnfNotification/tns:rollback/tns:msoRequest/tns:requestId/text()");
1105                         String serviceInstanceId = xpathTool.evaluate(
1106                                 "/tns:createVnfNotification/tns:rollback/tns:msoRequest/tns:serviceInstanceId/text()");
1107
1108                         if (requestId != null || serviceInstanceId != null) {
1109                                 MsoRequest msoRequest = new MsoRequest();
1110                                 msoRequest.setRequestId(requestId);
1111                                 msoRequest.setServiceInstanceId(serviceInstanceId);
1112                                 rollback.setMsoRequest(msoRequest);
1113                         }
1114
1115                         String tenantCreated = xpathTool.evaluate(
1116                                 "/tns:createVnfNotification/tns:rollback/tns:tenantCreated/text()");
1117                         rollback.setTenantCreated("true".equals(tenantCreated));
1118
1119                         String tenantId = xpathTool.evaluate(
1120                                 "/tns:createVnfNotification/tns:rollback/tns:tenantId/text()");
1121                         rollback.setTenantId(tenantId);
1122
1123                         String vnfCreated = xpathTool.evaluate(
1124                                 "/tns:createVnfNotification/tns:rollback/tns:vnfCreated/text()");
1125                         rollback.setVnfCreated("true".equals(vnfCreated));
1126
1127                         String rollbackVnfId = xpathTool.evaluate(
1128                                 "/tns:createVnfNotification/tns:rollback/tns:vnfId/text()");
1129                         rollback.setVnfId(rollbackVnfId);
1130
1131                         createVnfNotification.setRollback(rollback);
1132
1133                 } catch (Exception e) {
1134                         msoLogger.debug("Failed to unmarshal VNF callback content:");
1135                         msoLogger.debug(content);
1136                         return false;
1137                 }
1138
1139                 VnfAdapterNotifyServiceImpl notifyService = new VnfAdapterNotifyServiceImpl();
1140
1141
1142                 notifyService.createVnfNotification(
1143                         messageId,
1144                         createVnfNotification.isCompleted(),
1145                         createVnfNotification.getException(),
1146                         createVnfNotification.getErrorMessage(),
1147                         createVnfNotification.getVnfId(),
1148                         createVnfNotification.getOutputs(),
1149                         createVnfNotification.getRollback());
1150
1151                 return true;
1152         }
1153
1154         /**
1155          * Injects a Delete VNF adapter callback request. The specified callback data
1156          * may contain the placeholder string ((MESSAGE-ID)) which is replaced with
1157          * the actual message ID.  It may also contain the placeholder string
1158          * ((REQUEST-ID)) which is replaced request ID of the original MSO request.
1159          * @param content the content of the callback
1160          * @param timeout the timeout in milliseconds
1161          * @return true if the callback could be injected, false otherwise
1162          * @throws JAXBException if the content does not adhere to the schema
1163          */
1164         protected boolean injectDeleteVNFCallback(String content, long timeout) {
1165
1166                 String messageId = (String) getProcessVariable("vnfAdapterDeleteV1",
1167                         "VNFDEL_uuid", timeout);
1168
1169                 if (messageId == null) {
1170                         return false;
1171                 }
1172
1173                 content = content.replace("((MESSAGE-ID))", messageId);
1174                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
1175                 content = content.replace("{{MESSAGE-ID}}", messageId);
1176
1177                 msoLogger.debug("Injecting VNF adapter delete callback");
1178
1179                 // Is it possible to unmarshal this with JAXB?  I couldn't.
1180
1181                 DeleteVnfNotification deleteVnfNotification = new DeleteVnfNotification();
1182                 XPathTool xpathTool = new VnfNotifyXPathTool();
1183                 xpathTool.setXML(content);
1184
1185                 try {
1186                         String completed = xpathTool.evaluate(
1187                                 "/tns:deleteVnfNotification/tns:completed/text()");
1188                         deleteVnfNotification.setCompleted("true".equals(completed));
1189                         // if notification failure, set the exception and error message
1190                         if (deleteVnfNotification.isCompleted() == false) {
1191                                 deleteVnfNotification.setException(MsoExceptionCategory.INTERNAL);
1192                                 deleteVnfNotification.setErrorMessage(xpathTool.evaluate(
1193                                                 "/tns:deleteVnfNotification/tns:errorMessage/text()")) ;
1194                         }
1195
1196                 } catch (Exception e) {
1197                         msoLogger.debug("Failed to unmarshal VNF Delete callback content:");
1198                         msoLogger.debug(content);
1199                         return false;
1200                 }
1201
1202                 VnfAdapterNotifyServiceImpl notifyService = new VnfAdapterNotifyServiceImpl();
1203
1204
1205                 notifyService.deleteVnfNotification(
1206                         messageId,
1207                         deleteVnfNotification.isCompleted(),
1208                         deleteVnfNotification.getException(),
1209                         deleteVnfNotification.getErrorMessage());
1210
1211                 return true;
1212         }
1213
1214         /**
1215          * Injects a Update VNF adapter callback request. The specified callback data
1216          * may contain the placeholder string ((MESSAGE-ID)) which is replaced with
1217          * the actual message ID.  It may also contain the placeholder string
1218          * ((REQUEST-ID)) which is replaced request ID of the original MSO request.
1219          * @param content the content of the callback
1220          * @param timeout the timeout in milliseconds
1221          * @return true if the callback could be injected, false otherwise
1222          * @throws JAXBException if the content does not adhere to the schema
1223          */
1224         protected boolean injectUpdateVNFCallback(String content, long timeout) {
1225
1226                 String messageId = (String) getProcessVariable("vnfAdapterUpdate",
1227                         "VNFU_messageId", timeout);
1228
1229                 if (messageId == null) {
1230                         return false;
1231                 }
1232
1233                 content = content.replace("((MESSAGE-ID))", messageId);
1234                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
1235                 content = content.replace("{{MESSAGE-ID}}", messageId);
1236
1237                 content = content.replace("((REQUEST-ID))", msoRequestId);
1238                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
1239                 content = content.replace("{{REQUEST-ID}}", msoRequestId);
1240
1241                 msoLogger.debug("Injecting VNF adapter callback");
1242
1243                 // Is it possible to unmarshal this with JAXB?  I couldn't.
1244
1245                 UpdateVnfNotification updateVnfNotification = new UpdateVnfNotification();
1246                 XPathTool xpathTool = new VnfNotifyXPathTool();
1247                 xpathTool.setXML(content);
1248
1249                 try {
1250                         String completed = xpathTool.evaluate(
1251                                 "/tns:updateVnfNotification/tns:completed/text()");
1252                         updateVnfNotification.setCompleted("true".equals(completed));
1253
1254                         NodeList entries = (NodeList) xpathTool.evaluate(
1255                                 "/tns:updateVnfNotification/tns:outputs/tns:entry",
1256                                 XPathConstants.NODESET);
1257
1258                         UpdateVnfNotificationOutputs outputs = new UpdateVnfNotificationOutputs();
1259
1260                         for (int i = 0; i < entries.getLength(); i++) {
1261                                 Node node = entries.item(i);
1262
1263                                 if (node.getNodeType() == Node.ELEMENT_NODE) {
1264                                         Element entry = (Element) node;
1265                                         String key = entry.getElementsByTagNameNS("*", "key").item(0).getTextContent();
1266                                         String value = entry.getElementsByTagNameNS("*", "value").item(0).getTextContent();
1267                                         outputs.add(key, value);
1268                                 }
1269                         }
1270
1271                         updateVnfNotification.setOutputs(outputs);
1272
1273                         VnfRollback rollback = new VnfRollback();
1274
1275                         String cloudSiteId = xpathTool.evaluate(
1276                                 "/tns:updateVnfNotification/tns:rollback/tns:cloudSiteId/text()");
1277                         rollback.setCloudSiteId(cloudSiteId);
1278
1279                         String requestId = xpathTool.evaluate(
1280                                 "/tns:updateVnfNotification/tns:rollback/tns:msoRequest/tns:requestId/text()");
1281                         String serviceInstanceId = xpathTool.evaluate(
1282                                 "/tns:updateVnfNotification/tns:rollback/tns:msoRequest/tns:serviceInstanceId/text()");
1283
1284                         if (requestId != null || serviceInstanceId != null) {
1285                                 MsoRequest msoRequest = new MsoRequest();
1286                                 msoRequest.setRequestId(requestId);
1287                                 msoRequest.setServiceInstanceId(serviceInstanceId);
1288                                 rollback.setMsoRequest(msoRequest);
1289                         }
1290
1291                         String tenantCreated = xpathTool.evaluate(
1292                                 "/tns:updateVnfNotification/tns:rollback/tns:tenantCreated/text()");
1293                         rollback.setTenantCreated("true".equals(tenantCreated));
1294
1295                         String tenantId = xpathTool.evaluate(
1296                                 "/tns:updateVnfNotification/tns:rollback/tns:tenantId/text()");
1297                         rollback.setTenantId(tenantId);
1298
1299                         String vnfCreated = xpathTool.evaluate(
1300                                 "/tns:updateVnfNotification/tns:rollback/tns:vnfCreated/text()");
1301                         rollback.setVnfCreated("true".equals(vnfCreated));
1302
1303                         String rollbackVnfId = xpathTool.evaluate(
1304                                 "/tns:updateVnfNotification/tns:rollback/tns:vnfId/text()");
1305                         rollback.setVnfId(rollbackVnfId);
1306
1307                         updateVnfNotification.setRollback(rollback);
1308
1309                 } catch (Exception e) {
1310                         msoLogger.debug("Failed to unmarshal VNF callback content:");
1311                         msoLogger.debug(content);
1312                         return false;
1313                 }
1314
1315                 VnfAdapterNotifyServiceImpl notifyService = new VnfAdapterNotifyServiceImpl();
1316
1317
1318                 notifyService.updateVnfNotification(
1319                         messageId,
1320                         updateVnfNotification.isCompleted(),
1321                         updateVnfNotification.getException(),
1322                         updateVnfNotification.getErrorMessage(),
1323                         updateVnfNotification.getOutputs(),
1324                         updateVnfNotification.getRollback());
1325
1326                 return true;
1327         }
1328
1329         /**
1330          * Runs a program to inject workflow messages into the test environment.
1331          * A program is essentially just a list of keys that identify event data
1332          * to be injected, in sequence. An example program:
1333          * <pre>
1334          *     event1, event2
1335          * </pre>
1336          * Errors are handled with junit assertions and will cause the test to fail.
1337          * NOTE: Each callback must have a workflow message type associated with it.
1338          * @param callbacks an object containing event data for the program
1339          * @param program the program to execute
1340          */
1341         protected void injectWorkflowMessages(CallbackSet callbacks, String program) {
1342
1343                 String[] cmds = program.replaceAll("\\s+", "").split(",");
1344
1345                 for (String cmd : cmds) {
1346                         String action = cmd;
1347                         String modifier = "STD";
1348
1349                         if (cmd.contains(":")) {
1350                                 String[] parts = cmd.split(":");
1351                                 action = parts[0];
1352                                 modifier = parts[1];
1353                         }
1354
1355                         String messageType = null;
1356                         String content = null;
1357                         String contentType = null;
1358
1359                         if ("STD".equals(modifier)) {
1360                                 CallbackData callbackData = callbacks.get(action);
1361
1362                                 if (callbackData == null) {
1363                                         String msg = "No '" + action + "' workflow message callback is defined";
1364                                         msoLogger.debug(msg);
1365                                         fail(msg);
1366                                 }
1367
1368                                 messageType = callbackData.getMessageType();
1369
1370                                 if (messageType == null || messageType.trim().equals("")) {
1371                                         String msg = "No workflow message type is defined in the '" + action + "' callback";
1372                                         msoLogger.debug(msg);
1373                                         fail(msg);
1374                                 }
1375
1376                                 content = callbackData.getContent();
1377                                 contentType = callbackData.getContentType();
1378                         } else {
1379                                 String msg = "Invalid workflow message program modifier: '" + modifier + "'";
1380                                 msoLogger.debug(msg);
1381                                 fail(msg);
1382                         }
1383
1384                         if (!injectWorkflowMessage(contentType, messageType, content, 10000)) {
1385                                 fail("Failed to inject '" + action + "' workflow message");
1386                         }
1387
1388                         try {
1389                                 Thread.sleep(1000);
1390                         } catch (InterruptedException e) {
1391                                 fail("Interrupted after injection of '" + action + "' workflow message");
1392                         }
1393                 }
1394         }
1395
1396         /**
1397          * Injects a workflow message. The specified callback data may contain the
1398          * placeholder string ((CORRELATOR)) which is replaced with the actual
1399          * correlator value.
1400          * @param contentType the HTTP contentType for the message (possibly null)
1401          * @param messageType the message type
1402          * @param content the message content (possibly null)
1403          * @param timeout the timeout in milliseconds
1404          * @return true if the message could be injected, false otherwise
1405          */
1406         protected boolean injectWorkflowMessage(String contentType, String messageType, String content, long timeout) {
1407                 String correlator = (String) getProcessVariable("ReceiveWorkflowMessage",
1408                         messageType + "_CORRELATOR", timeout);
1409
1410                 if (correlator == null) {
1411                         return false;
1412                 }
1413
1414                 if (content != null) {
1415                         content = content.replace("((CORRELATOR))", correlator);
1416                 }
1417
1418                 msoLogger.debug("Injecting " + messageType + " message");
1419
1420                 Response response = workflowMessageResource.deliver(contentType, messageType, correlator, content);
1421                 msoLogger.debug("Workflow response to " + messageType + " message: " + response);
1422                 return true;
1423         }
1424
1425         /**
1426          * Runs a program to inject sniro workflow messages into the test environment.
1427          * A program is essentially just a list of keys that identify event data
1428          * to be injected, in sequence. For more details, see
1429          * injectSNIROCallbacks(String contentType, String messageType, String content, long timeout)
1430          *
1431          * Errors are handled with junit assertions and will cause the test to fail.
1432          * NOTE: Each callback must have a workflow message type associated with it.
1433          *
1434          * @param callbacks an object containing event data for the program
1435          * @param program the program to execute
1436          */
1437         protected void injectSNIROCallbacks(CallbackSet callbacks, String program) {
1438
1439                 String[] cmds = program.replaceAll("\\s+", "").split(",");
1440
1441                 for (String cmd : cmds) {
1442                         String action = cmd;
1443                         String modifier = "STD";
1444
1445                         if (cmd.contains(":")) {
1446                                 String[] parts = cmd.split(":");
1447                                 action = parts[0];
1448                                 modifier = parts[1];
1449                         }
1450
1451                         String messageType = null;
1452                         String content = null;
1453                         String contentType = null;
1454
1455                         if ("STD".equals(modifier)) {
1456                                 CallbackData callbackData = callbacks.get(action);
1457
1458                                 if (callbackData == null) {
1459                                         String msg = "No '" + action + "' workflow message callback is defined";
1460                                         msoLogger.debug(msg);
1461                                         fail(msg);
1462                                 }
1463
1464                                 messageType = callbackData.getMessageType();
1465
1466                                 if (messageType == null || messageType.trim().equals("")) {
1467                                         String msg = "No workflow message type is defined in the '" + action + "' callback";
1468                                         msoLogger.debug(msg);
1469                                         fail(msg);
1470                                 }
1471
1472                                 content = callbackData.getContent();
1473                                 contentType = callbackData.getContentType();
1474                         } else {
1475                                 String msg = "Invalid workflow message program modifier: '" + modifier + "'";
1476                                 msoLogger.debug(msg);
1477                                 fail(msg);
1478                         }
1479
1480                         if (!injectSNIROCallbacks(contentType, messageType, content, 10000)) {
1481                                 fail("Failed to inject '" + action + "' workflow message");
1482                         }
1483
1484                         try {
1485                                 Thread.sleep(1000);
1486                         } catch (InterruptedException e) {
1487                                 fail("Interrupted after injection of '" + action + "' workflow message");
1488                         }
1489                 }
1490         }
1491
1492         /**
1493          * Injects a sniro workflow message. The specified callback response may
1494          * contain the placeholder strings ((CORRELATOR)) and ((SERVICE_RESOURCE_ID))
1495          * The ((CORRELATOR)) is replaced with the actual correlator value from the
1496          * request. The ((SERVICE_RESOURCE_ID)) is replaced with the actual serviceResourceId
1497          * value from the sniro request. Currently this only works with sniro request
1498          * that contain only 1 resource.
1499          *
1500          * @param contentType the HTTP contentType for the message (possibly null)
1501          * @param messageType the message type
1502          * @param content the message content (possibly null)
1503          * @param timeout the timeout in milliseconds
1504          * @return true if the message could be injected, false otherwise
1505          */
1506         protected boolean injectSNIROCallbacks(String contentType, String messageType, String content, long timeout) {
1507                 String correlator = (String) getProcessVariable("ReceiveWorkflowMessage",
1508                         messageType + "_CORRELATOR", timeout);
1509
1510                 if (correlator == null) {
1511                         return false;
1512                 }
1513                 if (content != null) {
1514                         content = content.replace("((CORRELATOR))", correlator);
1515                         if(messageType.equalsIgnoreCase("SNIROResponse")){
1516                                 ServiceDecomposition decomp = (ServiceDecomposition) getProcessVariable("Homing", "serviceDecomposition", timeout);
1517                                 List<Resource> resourceList = decomp.getServiceResources();
1518                                 if(resourceList.size() == 1){
1519                                         String resourceId = "";
1520                                         for(Resource resource:resourceList){
1521                                                 resourceId = resource.getResourceId();
1522                                         }
1523                                         String homingList = getJsonValue(content, "solutionInfo.placementInfo");
1524                                         JSONArray placementArr = null;
1525                                         try {
1526                                                 placementArr = new JSONArray(homingList);
1527                                         }
1528                                         catch (Exception e) {
1529                                                 return false;
1530                                         }
1531                                         if(placementArr.length() == 1){
1532                                                 content = content.replace("((SERVICE_RESOURCE_ID))", resourceId);
1533                                         }
1534                                         String licenseInfoList = getJsonValue(content, "solutionInfo.licenseInfo");
1535                                         JSONArray licenseArr = null;
1536                                         try {
1537                                                 licenseArr = new JSONArray(licenseInfoList);
1538                                         }
1539                                         catch (Exception e) {
1540                                                 return false;
1541                                         }
1542                                         if(licenseArr.length() == 1){
1543                                                 content = content.replace("((SERVICE_RESOURCE_ID))", resourceId);
1544                                         }
1545                                 }
1546                                 else {
1547                                         try {
1548                                                 String homingList = getJsonValue(content, "solutionInfo.placementInfo");
1549                                                 String licenseInfoList = getJsonValue(content, "solutionInfo.licenseInfo");
1550                                                 JSONArray placementArr = new JSONArray(homingList);
1551                                                 JSONArray licenseArr = new JSONArray(licenseInfoList);
1552                                                 for (Resource resource: resourceList) {
1553                                                         String resourceModuleName = resource.getModelInfo().getModelInstanceName();
1554                                                         String resourceId = resource.getResourceId();
1555
1556                                                         for (int i=0; i<placementArr.length(); i++) {
1557                                                                 JSONObject placementObj = placementArr.getJSONObject(i);
1558                                                                 String placementModuleName = placementObj.getString("resourceModuleName");
1559                                                                 if (placementModuleName.equalsIgnoreCase(resourceModuleName)) {
1560                                                                         String placementString = placementObj.toString();
1561                                                                         placementString = placementString.replace("((SERVICE_RESOURCE_ID))", resourceId);
1562                                                                         JSONObject newPlacementObj = new JSONObject(placementString);
1563                                                                         placementArr.put(i, newPlacementObj);
1564                                                                 }
1565                                                         }
1566
1567                                                         for (int i=0; i<licenseArr.length(); i++) {
1568                                                                 JSONObject licenseObj = licenseArr.getJSONObject(i);
1569                                                                 String licenseModuleName = licenseObj.getString("resourceModuleName");
1570                                                                 if (licenseModuleName.equalsIgnoreCase(resourceModuleName)) {
1571                                                                         String licenseString = licenseObj.toString();
1572                                                                         licenseString = licenseString.replace("((SERVICE_RESOURCE_ID))", resourceId);
1573                                                                         JSONObject newLicenseObj = new JSONObject(licenseString);
1574                                                                         licenseArr.put(i, newLicenseObj);
1575                                                                 }
1576                                                         }
1577                                                 }
1578                                                 String newPlacementInfos = placementArr.toString();
1579                                                 String newLicenseInfos = licenseArr.toString();
1580                                                 content = updJsonValue(content, "solutionInfo.placementInfo", newPlacementInfos);
1581                                                 content = updJsonValue(content, "solutionInfo.licenseInfo", newLicenseInfos);
1582                                         }
1583                                         catch(Exception e) {
1584                                                 return false;
1585                                         }
1586
1587                                 }
1588                         }
1589                 }
1590                 msoLogger.debug("Injecting " + messageType + " message");
1591
1592                 Response response = workflowMessageResource.deliver(contentType, messageType, correlator, content);
1593                 msoLogger.debug("Workflow response to " + messageType + " message: " + response);
1594                 return true;
1595         }
1596
1597
1598         /**
1599          * Wait for the process to end.
1600          * @param businessKey the process business key
1601          * @param timeout the amount of time to wait, in milliseconds
1602          */
1603         protected void waitForProcessEnd(String businessKey, long timeout) {
1604                 msoLogger.debug("Waiting " + timeout + "ms for process with business key " +
1605                         businessKey + " to end");
1606
1607                 long now = System.currentTimeMillis() + timeout;
1608                 long endTime = now + timeout;
1609
1610                 while (now <= endTime) {
1611                         if (isProcessEnded(businessKey)) {
1612                                 msoLogger.debug("Process with business key " + businessKey + " has ended");
1613                                 return;
1614                         }
1615
1616                         try {
1617                                 Thread.sleep(200);
1618                         } catch (InterruptedException e) {
1619                                 String msg = "Interrupted waiting for process with business key " +
1620                                         businessKey + " to end";
1621                                 msoLogger.debug(msg);
1622                                 fail(msg);
1623                         }
1624
1625                         now = System.currentTimeMillis();
1626                 }
1627
1628                 String msg = "Process with business key " + businessKey +
1629                         " did not end within " + timeout + "ms";
1630                 msoLogger.debug(msg);
1631                 fail(msg);
1632         }
1633
1634         /**
1635          * Wait for the process to end. Must be used when multiple process instances exist with
1636          * this same business key such as when its passed to subflows or shared across multiple
1637          * processes.
1638          *
1639          * @param businessKey the process business key
1640          * @param processName the process definition name
1641          * @param timeout the amount of time to wait, in milliseconds
1642          * @author cb645j
1643          */
1644         protected void waitForProcessEnd(String businessKey, String processName, long timeout) {
1645                 msoLogger.debug("Waiting " + timeout + "ms for process with business key " +
1646                         businessKey + " to end");
1647
1648                 long now = System.currentTimeMillis() + timeout;
1649                 long endTime = now + timeout;
1650
1651                 while (now <= endTime) {
1652                         if (isProcessEnded(businessKey, processName)) {
1653                                 msoLogger.debug("Process with business key " + businessKey + " has ended");
1654                                 return;
1655                         }
1656
1657                         try {
1658                                 Thread.sleep(200);
1659                         } catch (InterruptedException e) {
1660                                 String msg = "Interrupted waiting for process with business key " +
1661                                         businessKey + " to end";
1662                                 msoLogger.debug(msg);
1663                                 fail(msg);
1664                         }
1665
1666                         now = System.currentTimeMillis();
1667                 }
1668
1669                 String msg = "Process with business key " + businessKey +
1670                         " did not end within " + timeout + "ms";
1671                 msoLogger.debug(msg);
1672                 fail(msg);
1673         }
1674
1675         /**
1676          * Verifies that the specified historic process variable has the specified value.
1677          * If the variable does not have the specified value, the test is failed.
1678          *
1679          * @param businessKey the process business key
1680          * @param variable the variable name
1681          * @param value the expected variable value
1682          */
1683         protected void checkVariable(String businessKey, String variable, Object value) {
1684                 if (!isProcessEnded(businessKey)) {
1685                         fail("Cannot get historic variable " + variable + " because process with business key " +
1686                                 businessKey + " has not ended");
1687                 }
1688
1689                 Object variableValue = getVariableFromHistory(businessKey, variable);
1690                 assertEquals(value, variableValue);
1691         }
1692
1693         /**
1694          * Checks to see if the specified process is ended.
1695          * @param businessKey the process business Key
1696          * @return true if the process is ended
1697          */
1698         protected boolean isProcessEnded(String businessKey) {
1699                 HistoricProcessInstance processInstance = historyService
1700                         .createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).singleResult();
1701                 return processInstance != null && processInstance.getEndTime() != null;
1702         }
1703
1704         /**
1705          * Checks to see if the specified process is ended.
1706          *
1707          * @param processInstanceId the process Instance Id
1708          * @return true if the process is ended
1709          */
1710         protected boolean isProcessEndedByProcessInstanceId(String processInstanceId) {
1711                 HistoricProcessInstance processInstance = historyService
1712                                 .createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
1713                 return processInstance != null && processInstance.getEndTime() != null;
1714         }
1715
1716         /**
1717          * Checks to see if the specified process is ended.
1718          *
1719          * @author cb645j
1720          */
1721         //TODO combine into 1
1722         private boolean isProcessEnded(String businessKey, String processName) {
1723                 HistoricProcessInstance processInstance = historyService
1724                         .createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).processDefinitionName(processName).singleResult();
1725                 return processInstance != null && processInstance.getEndTime() != null;
1726         }
1727
1728         /**
1729          * Gets a variable value from a historical process instance. The business key must be unique.
1730          *
1731          * @param businessKey the process business key
1732          * @param variableName the variable name
1733          * @return the variable value or null if the variable does not exist
1734          */
1735         protected Object getVariableFromHistory(String businessKey, String variableName) {
1736                 try {
1737                         HistoricProcessInstance processInstance = historyService
1738                                 .createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).singleResult();
1739
1740                         if (processInstance == null) {
1741                                 return null;
1742                         }
1743
1744                         HistoricVariableInstance v = historyService
1745                                 .createHistoricVariableInstanceQuery().processInstanceId(processInstance.getId())
1746                                 .variableName(variableName).singleResult();
1747                         return v == null ? null : v.getValue();
1748                 } catch (Exception e) {
1749                         msoLogger.debug("Error retrieving variable " + variableName +
1750                                 " from historical process with business key " + businessKey + ": " + e);
1751                         return null;
1752                 }
1753         }
1754
1755         /**
1756          * Gets a variable value from a process instance based on businessKey and process name.
1757          * Must be used when multiple instances exist with the same business key such as when
1758          * business key is passed to subflows or shared across multiple processes. This method
1759          * can obtain variables from mainflows and from subflows.
1760          *
1761          * @param businessKey the process business key
1762          * @param processName the process definition name
1763          * @param variableName the variable name
1764          * @return the variable value or null if the variable does not exist
1765          * @author cb645j
1766          */
1767         protected Object getVariableFromHistory(String businessKey, String processName, String variableName){
1768                 try{
1769                         HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).processDefinitionName(processName)
1770                                         .singleResult();
1771
1772                         if(processInstance == null){
1773                                 return null;
1774                         }
1775                         HistoricVariableInstance variable = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstance.getId()).variableName(variableName).singleResult();
1776
1777                         return variable == null ? null : variable.getValue();
1778                 }catch(ProcessEngineException e){
1779                         msoLogger.debug("Multiple proccess instances exist with process name " + processName + " and business key " + businessKey + ". Must pass instance index as a parameter.");
1780                         return null;
1781                 }catch(Exception e){
1782                         msoLogger.debug("Error retrieving variable " + variableName + " from historical process for process " + processName + " with business key " + businessKey + ": " + e);
1783                         return null;
1784                 }
1785         }
1786
1787         /**
1788          * Gets the value of a process variable from x instance of y process. Must be used when
1789          * multiple instances exist with the same business key AND process name. This method
1790          * shall be used primarily for obtaining subflow variables when the business key is
1791          * passed to the subflow AND the subflow is called multiple times in a given flow.
1792          *
1793          * @param businessKey the process business key
1794          * @param processName the name of the subflow that contains the variable
1795          * @param variableName the variable name
1796          * @param processInstanceIndex the instance in which the subprocess was called
1797          * @return the variable value or null if the variable does not exist
1798          * @author cb645j
1799          */
1800         protected Object getVariableFromHistory(String businessKey, int subflowInstanceIndex, String processName, String variableName){
1801                 try{
1802                         List<HistoricProcessInstance> processInstanceList = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).processDefinitionName(processName)
1803                                         .list();
1804
1805                         if(processInstanceList == null){
1806                                 return null;
1807                         }
1808                         processInstanceList.sort((m1, m2) -> m1.getStartTime().compareTo(m2.getStartTime()));
1809
1810                         HistoricProcessInstance processInstance = processInstanceList.get(subflowInstanceIndex);
1811                         HistoricVariableInstance variable = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstance.getId())
1812                                         .variableName(variableName).singleResult();
1813
1814                         return variable == null ? null : variable.getValue();
1815                 }catch(Exception e) {
1816                         msoLogger.debug("Error retrieving variable " + variableName + " from historical process for process " + processName + " with business key " + businessKey + ": " + e);
1817                         return null;
1818                 }
1819         }
1820
1821
1822         /**
1823          * Gets the value of a subflow variable from the specified subflow's
1824          * historical process instance.
1825          *
1826          * DEPRECATED - Use method getVariableFromHistory(businessKey, processName, variableName) instead
1827          *
1828          * @param subflowName - the name of the subflow that contains the variable
1829          * @param variableName the variable name
1830          *
1831          * @return the variable value, or null if the variable could not be obtained
1832          *
1833          */
1834         @Deprecated
1835         protected Object getVariableFromSubflowHistory(String subflowName, String variableName) {
1836                 try {
1837                         List<HistoricProcessInstance> processInstanceList = historyService
1838                                         .createHistoricProcessInstanceQuery().processDefinitionName(subflowName).list();
1839
1840                         if (processInstanceList == null) {
1841                                 return null;
1842                         }
1843
1844                         processInstanceList.sort((m1, m2) -> m1.getStartTime().compareTo(m2.getStartTime()));
1845
1846                         HistoricProcessInstance processInstance = processInstanceList.get(0);
1847
1848                         HistoricVariableInstance v = historyService
1849                                 .createHistoricVariableInstanceQuery().processInstanceId(processInstance.getId())
1850                                 .variableName(variableName).singleResult();
1851                         return v == null ? null : v.getValue();
1852                 } catch (Exception e) {
1853                         msoLogger.debug("Error retrieving variable " + variableName +
1854                                         " from sub flow: " + subflowName + ", Exception is: " + e);
1855                         return null;
1856                 }
1857         }
1858
1859         /**
1860          * Gets the value of a subflow variable from the subflow's
1861          * historical process x instance.
1862          *
1863          * DEPRECATED: Use method getVariableFromHistory(businessKey, processInstanceIndex, processName, variableName) instead
1864          *
1865          * @param subflowName - the name of the subflow that contains the variable
1866          * @param variableName the variable name
1867          * @param subflowInstanceIndex - the instance of the subflow (use when same subflow is called more than once from mainflow)
1868          *
1869          * @return the variable value, or null if the variable could not be obtained
1870          */
1871         @Deprecated
1872         protected Object getVariableFromSubflowHistory(int subflowInstanceIndex, String subflowName, String variableName) {
1873                 try {
1874                         List<HistoricProcessInstance> processInstanceList = historyService.createHistoricProcessInstanceQuery().processDefinitionName(subflowName).list();
1875
1876                         if (processInstanceList == null) {
1877                                 return null;
1878                         }
1879
1880                         processInstanceList.sort((m1, m2) -> m1.getStartTime().compareTo(m2.getStartTime()));
1881
1882                         HistoricProcessInstance processInstance = processInstanceList.get(subflowInstanceIndex);
1883
1884                         HistoricVariableInstance v = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstance.getId())
1885                                 .variableName(variableName).singleResult();
1886                         return v == null ? null : v.getValue();
1887                 } catch (Exception e) {
1888                         msoLogger.debug("Error retrieving variable " + variableName +
1889                                 " from " + subflowInstanceIndex + " instance index of sub flow: " + subflowName + ", Exception is: " + e);
1890                         return null;
1891                 }
1892         }
1893
1894         /**
1895          * Extracts text from an XML element. This method is not namespace aware
1896          * (namespaces are ignored).  The first matching element is selected.
1897          * @param xml the XML document or fragment
1898          * @param tag the desired element, e.g. "<name>"
1899          * @return the element text, or null if the element was not found
1900          */
1901         protected String getXMLTextElement(String xml, String tag) {
1902                 xml = removeXMLNamespaces(xml);
1903
1904                 if (!tag.startsWith("<")) {
1905                         tag = "<" + tag + ">";
1906                 }
1907
1908                 int start = xml.indexOf(tag);
1909
1910                 if (start == -1) {
1911                         return null;
1912                 }
1913
1914                 int end = xml.indexOf('<', start + tag.length());
1915
1916                 if (end == -1) {
1917                         return null;
1918                 }
1919
1920                 return xml.substring(start + tag.length(), end);
1921         }
1922
1923         /**
1924          * Removes namespace definitions and prefixes from XML, if any.
1925          */
1926         private String removeXMLNamespaces(String xml) {
1927                 // remove xmlns declaration
1928                 xml = xml.replaceAll("xmlns.*?(\"|\').*?(\"|\')", "");
1929
1930                 // remove opening tag prefix
1931                 xml = xml.replaceAll("(<)(\\w+:)(.*?>)", "$1$3");
1932
1933                 // remove closing tags prefix
1934                 xml = xml.replaceAll("(</)(\\w+:)(.*?>)", "$1$3");
1935
1936                 // remove extra spaces left when xmlns declarations are removed
1937                 xml = xml.replaceAll("\\s+>", ">");
1938
1939                 return xml;
1940         }
1941
1942         /**
1943          * Asserts that two XML documents are semantically equivalent.  Differences
1944          * in whitespace or in namespace usage do not affect the comparison.
1945          * @param expected the expected XML
1946          * @param actual the XML to test
1947          * @throws SAXException
1948          * @throws IOException
1949          */
1950     public static void assertXMLEquals(String expected, String actual)
1951                 throws SAXException, IOException {
1952         XMLUnit.setIgnoreWhitespace(true);
1953         XMLUnit.setIgnoreAttributeOrder(true);
1954         DetailedDiff diff = new DetailedDiff(XMLUnit.compareXML(expected, actual));
1955         List<?> allDifferences = diff.getAllDifferences();
1956         assertEquals("Differences found: " + diff.toString(), 0, allDifferences.size());
1957     }
1958
1959         /**
1960          * A test implementation of AsynchronousResponse.
1961          */
1962         public class TestAsyncResponse {
1963                 Response response = null;
1964
1965                 /**
1966                  * {@inheritDoc}
1967                  */
1968                 public synchronized void setResponse(Response response) {
1969                         this.response = response;
1970                 }
1971
1972                 /**
1973                  * Gets the response.
1974                  * @return the response, or null if none has been produced yet
1975                  */
1976                 public synchronized Response getResponse() {
1977                         return response;
1978                 }
1979         }
1980
1981         /**
1982          * An object that contains callback data for a "program".
1983          */
1984         public class CallbackSet {
1985                 private final Map<String, CallbackData> map = new HashMap<>();
1986
1987                 /**
1988                  * Add untyped callback data to the set.
1989                  * @param action the action with which the data is associated
1990                  * @param content the callback data
1991                  */
1992                 public void put(String action, String content) {
1993                         map.put(action, new CallbackData(null, null, content));
1994                 }
1995
1996                 /**
1997                  * Add callback data to the set.
1998                  * @param action the action with which the data is associated
1999                  * @param messageType the callback message type
2000                  * @param content the callback data
2001                  */
2002                 public void put(String action, String messageType, String content) {
2003                         map.put(action, new CallbackData(null, messageType, content));
2004                 }
2005
2006                 /**
2007                  * Add callback data to the set.
2008                  * @param action the action with which the data is associated
2009                  * @param contentType the callback HTTP content type
2010                  * @param messageType the callback message type
2011                  * @param content the callback data
2012                  */
2013                 public void put(String action, String contentType, String messageType, String content) {
2014                         map.put(action, new CallbackData(contentType, messageType, content));
2015                 }
2016
2017                 /**
2018                  * Retrieve callback data from the set.
2019                  * @param action the action with which the data is associated
2020                  * @return the callback data, or null if there is none for the specified operation
2021                  */
2022                 public CallbackData get(String action) {
2023                         return map.get(action);
2024                 }
2025         }
2026
2027         /**
2028          * Represents a callback data item.
2029          */
2030         public class CallbackData {
2031                 private final String contentType;
2032                 private final String messageType;
2033                 private final String content;
2034
2035                 /**
2036                  * Constructor
2037                  * @param contentType the HTTP content type (optional)
2038                  * @param messageType the callback message type (optional)
2039                  * @param content the content
2040                  */
2041                 public CallbackData(String contentType, String messageType, String content) {
2042                         this.contentType = contentType;
2043                         this.messageType = messageType;
2044                         this.content = content;
2045                 }
2046
2047                 /**
2048                  * Gets the callback HTTP content type, possibly null.
2049                  */
2050                 public String getContentType() {
2051                         return contentType;
2052                 }
2053
2054                 /**
2055                  * Gets the callback message type, possibly null.
2056                  */
2057                 public String getMessageType() {
2058                         return messageType;
2059                 }
2060
2061                 /**
2062                  * Gets the callback content.
2063                  */
2064                 public String getContent() {
2065                         return content;
2066                 }
2067         }
2068
2069         /**
2070          * A tool for evaluating XPath expressions.
2071          */
2072         protected class XPathTool {
2073                 private final DocumentBuilderFactory factory;
2074                 private final SimpleNamespaceContext context = new SimpleNamespaceContext();
2075                 private final XPath xpath = XPathFactory.newInstance().newXPath();
2076                 private String xml = null;
2077                 private Document doc = null;
2078
2079                 /**
2080                  * Constructor.
2081                  */
2082                 public XPathTool() {
2083                         factory = DocumentBuilderFactory.newInstance();
2084                         factory.setNamespaceAware(true);
2085                         xpath.setNamespaceContext(context);
2086                 }
2087
2088                 /**
2089                  * Adds a namespace.
2090                  * @param prefix the namespace prefix
2091                  * @param uri the namespace uri
2092                  */
2093                 public synchronized void addNamespace(String prefix, String uri) {
2094                         context.add(prefix, uri);
2095                 }
2096
2097                 /**
2098                  * Sets the XML content to be operated on.
2099                  * @param xml the XML content
2100                  */
2101                 public synchronized void setXML(String xml) {
2102                         this.xml = xml;
2103                         this.doc = null;
2104                 }
2105
2106                 /**
2107                  * Returns the document object.
2108                  * @return the document object, or null if XML has not been set
2109                  * @throws SAXException
2110                  * @throws IOException
2111                  * @throws ParserConfigurationException
2112                  */
2113                 public synchronized Document getDocument()
2114                                 throws ParserConfigurationException, IOException, SAXException {
2115                         if (xml == null) {
2116                                 return null;
2117                         }
2118
2119                         buildDocument();
2120                         return doc;
2121                 }
2122
2123                 /**
2124                  * Evaluates the specified XPath expression and returns a string result.
2125                  * This method throws exceptions on error.
2126                  * @param expression the expression
2127                  * @return the result object
2128                  * @throws ParserConfigurationException
2129                  * @throws IOException
2130                  * @throws SAXException
2131                  * @throws XPathExpressionException on error
2132                  */
2133                 public synchronized String evaluate(String expression)
2134                                 throws ParserConfigurationException, SAXException,
2135                                 IOException, XPathExpressionException {
2136                         return (String) evaluate(expression, XPathConstants.STRING);
2137                 }
2138
2139                 /**
2140                  * Evaluates the specified XPath expression.
2141                  * This method throws exceptions on error.
2142                  * @param expression the expression
2143                  * @param returnType the return type
2144                  * @return the result object
2145                  * @throws ParserConfigurationException
2146                  * @throws IOException
2147                  * @throws SAXException
2148                  * @throws XPathExpressionException on error
2149                  */
2150                 public synchronized Object evaluate(String expression, QName returnType)
2151                                 throws ParserConfigurationException, SAXException,
2152                                 IOException, XPathExpressionException {
2153
2154                         buildDocument();
2155                         XPathExpression expr = xpath.compile(expression);
2156                         return expr.evaluate(doc, returnType);
2157                 }
2158
2159                 /**
2160                  * Private helper method that builds the document object.
2161                  * Assumes the calling method is synchronized.
2162                  * @throws ParserConfigurationException
2163                  * @throws IOException
2164                  * @throws SAXException
2165                  */
2166                 private void buildDocument() throws ParserConfigurationException,
2167                                 IOException, SAXException {
2168                         if (doc == null) {
2169                                 if (xml == null) {
2170                                         throw new IOException("XML input is null");
2171                                 }
2172
2173                                 DocumentBuilder builder = factory.newDocumentBuilder();
2174                                 InputSource source = new InputSource(new StringReader(xml));
2175                                 doc = builder.parse(source);
2176                         }
2177                 }
2178         }
2179
2180         /**
2181          * A NamespaceContext class based on a Map.
2182          */
2183         private class SimpleNamespaceContext implements NamespaceContext {
2184                 private Map<String, String> prefixMap = new HashMap<>();
2185                 private Map<String, String> uriMap = new HashMap<>();
2186
2187                 public synchronized void add(String prefix, String uri) {
2188                         prefixMap.put(prefix, uri);
2189                         uriMap.put(uri, prefix);
2190                 }
2191
2192                 @Override
2193                 public synchronized String getNamespaceURI(String prefix) {
2194                         return prefixMap.get(prefix);
2195                 }
2196
2197                 @Override
2198                 public Iterator<String> getPrefixes(String uri) {
2199                         List<String> list = new ArrayList<>();
2200                         String prefix = uriMap.get(uri);
2201                         if (prefix != null) {
2202                                 list.add(prefix);
2203                         }
2204                         return list.iterator();
2205                 }
2206
2207                 @Override
2208                 public String getPrefix(String uri) {
2209                         return uriMap.get(uri);
2210                 }
2211         }
2212
2213         /**
2214          * A VnfNotify XPathTool.
2215          */
2216         protected class VnfNotifyXPathTool extends XPathTool {
2217                 public VnfNotifyXPathTool() {
2218                         addNamespace("tns", "http://org.onap.so/vnfNotify");
2219                 }
2220         }
2221
2222         /**
2223          * Helper class to make it easier to create this type.
2224          */
2225         private static class CreateVnfNotificationOutputs
2226                         extends CreateVnfNotification.Outputs {
2227                 public void add(String key, String value) {
2228                         Entry entry = new Entry();
2229                         entry.setKey(key);
2230                         entry.setValue(value);
2231                         getEntry().add(entry);
2232                 }
2233         }
2234
2235         /**
2236          * Helper class to make it easier to create this type.
2237          */
2238         private static class UpdateVnfNotificationOutputs
2239                         extends UpdateVnfNotification.Outputs {
2240                 public void add(String key, String value) {
2241                         Entry entry = new Entry();
2242                         entry.setKey(key);
2243                         entry.setValue(value);
2244                         getEntry().add(entry);
2245                 }
2246         }
2247 }