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