[MSO-8] Update the maven dependency
[so.git] / bpmn / MSOCommonBPMN / src / test / java / org / openecomp / mso / bpmn / common / WorkflowTest.java
1 /*- 
2  * ============LICENSE_START======================================================= 
3  * OPENECOMP - MSO 
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.openecomp.mso.bpmn.common;
22
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.fail;
25
26 import java.io.IOException;
27 import java.io.StringReader;
28 import java.lang.management.ManagementFactory;
29 import java.lang.management.RuntimeMXBean;
30 import java.lang.reflect.Field;
31 import java.lang.reflect.Modifier;
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.Comparator;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.UUID;
40
41 import javax.ws.rs.core.Response;
42 import javax.xml.bind.JAXBException;
43 import javax.xml.namespace.NamespaceContext;
44 import javax.xml.namespace.QName;
45 import javax.xml.parsers.DocumentBuilder;
46 import javax.xml.parsers.DocumentBuilderFactory;
47 import javax.xml.parsers.ParserConfigurationException;
48 import javax.xml.xpath.XPath;
49 import javax.xml.xpath.XPathConstants;
50 import javax.xml.xpath.XPathExpression;
51 import javax.xml.xpath.XPathExpressionException;
52 import javax.xml.xpath.XPathFactory;
53
54 import org.camunda.bpm.engine.RuntimeService;
55 import org.camunda.bpm.engine.history.HistoricProcessInstance;
56 import org.camunda.bpm.engine.history.HistoricVariableInstance;
57 import org.camunda.bpm.engine.runtime.ProcessInstance;
58 import org.camunda.bpm.engine.test.ProcessEngineRule;
59 import org.camunda.bpm.engine.variable.impl.VariableMapImpl;
60 import org.custommonkey.xmlunit.DetailedDiff;
61 import org.custommonkey.xmlunit.XMLUnit;
62 import org.jboss.resteasy.spi.AsynchronousResponse;
63 import org.junit.Before;
64 import org.junit.Rule;
65 import org.openecomp.mso.bpmn.common.adapter.sdnc.CallbackHeader;
66 import org.openecomp.mso.bpmn.common.adapter.sdnc.SDNCAdapterCallbackRequest;
67 import org.openecomp.mso.bpmn.common.adapter.sdnc.SDNCAdapterResponse;
68 import org.openecomp.mso.bpmn.common.adapter.vnf.CreateVnfNotification;
69 import org.openecomp.mso.bpmn.common.adapter.vnf.DeleteVnfNotification;
70 import org.openecomp.mso.bpmn.common.adapter.vnf.MsoExceptionCategory;
71 import org.openecomp.mso.bpmn.common.adapter.vnf.MsoRequest;
72 import org.openecomp.mso.bpmn.common.adapter.vnf.UpdateVnfNotification;
73 import org.openecomp.mso.bpmn.common.adapter.vnf.VnfRollback;
74 import org.openecomp.mso.bpmn.common.workflow.service.SDNCAdapterCallbackServiceImpl;
75 import org.openecomp.mso.bpmn.common.workflow.service.VnfAdapterNotifyServiceImpl;
76 import org.openecomp.mso.bpmn.common.workflow.service.WorkflowAsyncCommonResource;
77 import org.openecomp.mso.bpmn.common.workflow.service.WorkflowMessageResource;
78 import org.openecomp.mso.bpmn.common.workflow.service.WorkflowResponse;
79 import org.openecomp.mso.bpmn.core.CamundaDBSetup;
80 import org.openecomp.mso.bpmn.core.PropertyConfigurationSetup;
81 import org.w3c.dom.Document;
82 import org.w3c.dom.Element;
83 import org.w3c.dom.Node;
84 import org.w3c.dom.NodeList;
85 import org.xml.sax.InputSource;
86 import org.xml.sax.SAXException;
87
88 import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
89 import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
90 import com.github.tomakehurst.wiremock.junit.WireMockRule;
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 public class WorkflowTest {
106         @Rule
107         public final ProcessEngineRule processEngineRule = new ProcessEngineRule();
108
109         @Rule
110         public final WireMockRule wireMockRule;
111
112         /**
113          * Constructor.
114          */
115         public WorkflowTest() throws RuntimeException {
116                 // Process WorkflowTestTransformer annotations
117                 List<ResponseTransformer> transformerList = new ArrayList<ResponseTransformer>();
118
119                 for (Field field : getClass().getFields()) {
120                         WorkflowTestTransformer annotation = (WorkflowTestTransformer)
121                                 field.getAnnotation(WorkflowTestTransformer.class);
122
123                         if (annotation == null) {
124                                 continue;
125                         }
126
127                         if (!Modifier.isStatic(field.getModifiers())) {
128                                 throw new RuntimeException(field.getDeclaringClass().getName()
129                                         + "#" + field.getName() + " has a @WorkflowTestTransformer "
130                                         + " annotation but it is not declared static");
131                         }
132
133                         ResponseTransformer transformer;
134
135                         try {
136                                 transformer = (ResponseTransformer) field.get(null);
137                         } catch (IllegalAccessException e) {
138                                 throw new RuntimeException(field.getDeclaringClass().getName()
139                                         + "#" + field.getName() + " is not accessible", e);
140                         } catch (ClassCastException e) {
141                                 throw new RuntimeException(field.getDeclaringClass().getName()
142                                         + "#" + field.getName() + " is not a ResponseTransformer", e);
143                         }
144
145                         if (transformer == null) {
146                                 continue;
147                         }
148
149                         transformerList.add(transformer);
150                 }
151
152                 ResponseTransformer[] transformerArray =
153                         transformerList.toArray(new ResponseTransformer[transformerList.size()]);
154
155                 wireMockRule = new WireMockRule(WireMockConfiguration.wireMockConfig()
156                         .port(28090).extensions(transformerArray));
157         }
158
159         @Before
160         public void testSetup() throws Exception {
161                 CamundaDBSetup.configure();
162                 PropertyConfigurationSetup.init();
163         }
164
165         /**
166          * The current request ID.  Normally set when an "invoke" method is called.
167          */
168         protected volatile String msoRequestId = null;
169
170         /**
171          * The current service instance ID.  Normally set when an "invoke" method
172          * is called.
173          */
174         protected volatile String msoServiceInstanceId = null;
175
176         /**
177          * Logs a test start method.
178          */
179         protected void logStart() {
180                 StackTraceElement[] st = Thread.currentThread().getStackTrace();
181                 String method = st[2].getMethodName();
182                 System.out.println("STARTED TEST: " + method);
183         }
184
185         /**
186          * Logs a test end method.
187          */
188         protected void logEnd() {
189                 StackTraceElement[] st = Thread.currentThread().getStackTrace();
190                 String method = st[2].getMethodName();
191                 System.out.println("ENDED TEST: " + method);
192         }
193
194         /**
195          * Invokes a subprocess.
196          * @param processKey the process key
197          * @param businessKey a unique key that will identify the process instance
198          * @param injectedVariables variables to inject into the process
199          */
200         protected void invokeSubProcess(String processKey, String businessKey,
201                         Map<String, Object> injectedVariables) {
202                 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
203                 List<String> arguments = runtimeMxBean.getInputArguments();
204                 System.out.println("JVM args = " + arguments);
205
206                 msoRequestId = (String) injectedVariables.get("mso-request-id");
207
208                 if (msoRequestId == null) {
209                         String msg = "mso-request-id variable was not provided";
210                         System.out.println(msg);
211                         fail(msg);
212                 }
213
214                 // Note: some scenarios don't have a service-instance-id, may be null
215                 msoServiceInstanceId = (String) injectedVariables.get("mso-service-instance-id");
216
217                 RuntimeService runtimeService = processEngineRule.getRuntimeService();
218                 runtimeService.startProcessInstanceByKey(processKey, businessKey, injectedVariables);
219         }
220
221         /**
222          * Invokes an asynchronous process.
223          * Errors are handled with junit assertions and will cause the test to fail.
224          * @param processKey the process key
225          * @param schemaVersion the API schema version, e.g. "v1"
226          * @param businessKey a unique key that will identify the process instance
227          * @param request the request
228          * @return a TestAsyncResponse object associated with the test
229          */
230         protected TestAsyncResponse invokeAsyncProcess(String processKey,
231                         String schemaVersion, String businessKey, String request) {
232                 return invokeAsyncProcess(processKey, schemaVersion, businessKey, request, null);
233         }
234
235         /**
236          * Invokes an asynchronous process.
237          * Errors are handled with junit assertions and will cause the test to fail.
238          * @param processKey the process key
239          * @param schemaVersion the API schema version, e.g. "v1"
240          * @param businessKey a unique key that will identify the process instance
241          * @param request the request
242          * @param injectedVariables optional variables to inject into the process
243          * @return a TestAsyncResponse object associated with the test
244          */
245         public TestAsyncResponse invokeAsyncProcess(String processKey,
246                         String schemaVersion, String businessKey, String request,
247                         Map<String, Object> injectedVariables) {
248
249                 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
250                 List<String> arguments = runtimeMxBean.getInputArguments();
251                 System.out.println("JVM args = " + arguments);
252
253                 Map<String, Object> variables = createVariables(schemaVersion, businessKey,
254                         request, injectedVariables, false);
255                 VariableMapImpl variableMapImpl = createVariableMapImpl(variables);
256
257                 System.out.println("Sending " + request + " to " + processKey + " process");
258                 WorkflowAsyncCommonResource workflowResource = new WorkflowAsyncCommonResource();
259                 workflowResource.setProcessEngineServices4junit(processEngineRule);
260
261                 TestAsyncResponse asyncResponse = new TestAsyncResponse();
262                 workflowResource.startProcessInstanceByKey(asyncResponse, processKey, variableMapImpl);
263                 return asyncResponse;
264         }
265
266         /**
267          * Invokes an asynchronous process.
268          * Errors are handled with junit assertions and will cause the test to fail.
269          * @param processKey the process key
270          * @param schemaVersion the API schema version, e.g. "v1"
271          * @param businessKey a unique key that will identify the process instance
272          * @param request the request
273          * @param injectedVariables optional variables to inject into the process
274          * @param serviceInstantiationModel indicates whether this method is being
275          * invoked for a flow that is designed using the service instantiation model
276          * @return a TestAsyncResponse object associated with the test
277          */
278         protected TestAsyncResponse invokeAsyncProcess(String processKey,
279                         String schemaVersion, String businessKey, String request,
280                         Map<String, Object> injectedVariables, boolean serviceInstantiationModel) {
281
282                 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
283                 List<String> arguments = runtimeMxBean.getInputArguments();
284                 System.out.println("JVM args = " + arguments);
285
286                 Map<String, Object> variables = createVariables(schemaVersion, businessKey,
287                         request, injectedVariables, serviceInstantiationModel);
288                 VariableMapImpl variableMapImpl = createVariableMapImpl(variables);
289
290                 System.out.println("Sending " + request + " to " + processKey + " process");
291                 WorkflowAsyncCommonResource workflowResource = new WorkflowAsyncCommonResource();
292                 workflowResource.setProcessEngineServices4junit(processEngineRule);
293
294                 TestAsyncResponse asyncResponse = new TestAsyncResponse();
295                 workflowResource.startProcessInstanceByKey(asyncResponse, processKey, variableMapImpl);
296                 return asyncResponse;
297         }
298
299         /**
300          * Private helper method that creates a variable map for a request.
301          * Errors are handled with junit assertions and will cause the test to fail.
302          * @param schemaVersion the API schema version, e.g. "v1"
303          * @param businessKey a unique key that will identify the process instance
304          * @param request the request
305          * @param injectedVariables optional variables to inject into the process
306          * @param serviceInstantiationModel indicates whether this method is being
307          * invoked for a flow that is designed using the service instantiation model
308          * @return a variable map
309          */
310         private Map<String, Object> createVariables(String schemaVersion,
311                         String businessKey, String request, Map<String, Object> injectedVariables,
312                         boolean serviceInstantiationModel) {
313
314                 Map<String, Object> variables = new HashMap<String, Object>();
315
316                 // These variables may be overridded by injected variables.
317                 variables.put("mso-service-request-timeout", "180");
318                 variables.put("isDebugLogEnabled", "true");
319
320                 // These variables may not be overridded by injected variables.
321                 String[] notAllowed = new String[] {
322                                 "mso-schema-version",
323                                 "mso-business-key",
324                                 "bpmnRequest",
325                                 "mso-request-id",
326                                 "mso-service-instance-id"
327                 };
328
329                 if (injectedVariables != null) {
330                         for (String key : injectedVariables.keySet()) {
331                                 for (String var : notAllowed) {
332                                         if (var.equals(key)) {
333                                                 String msg = "Cannot specify " + var + " in injected variables";
334                                                 System.out.println(msg);
335                                                 fail(msg);
336                                         }
337                                 }
338
339                                 variables.put(key, injectedVariables.get(key));
340                         }
341                 }
342
343                 variables.put("mso-schema-version", schemaVersion);
344                 variables.put("mso-business-key", businessKey);
345                 variables.put("bpmnRequest", request);
346
347                 if (serviceInstantiationModel) {
348
349                         /*
350                          * The request ID and the service instance ID are generated for flows
351                          * that follow the service instantiation model unless "requestId" and
352                          * "serviceInstanceId" are injected variables.
353                          */
354
355                         try {
356                                 msoRequestId = (String) injectedVariables.get("requestId");
357                                 variables.put("mso-request-id", msoRequestId);
358                                 msoServiceInstanceId = (String) injectedVariables.get("serviceInstanceId");
359                                 variables.put("mso-service-instance-id", msoServiceInstanceId);
360                         }
361                         catch(Exception e) {
362                         }
363                         if (msoRequestId == null || msoRequestId.trim().equals("")) {
364                                 System.out.println("No requestId element in injectedVariables");
365                                 variables.put("mso-request-id", UUID.randomUUID().toString());
366                         }
367                         if (msoServiceInstanceId == null || msoServiceInstanceId.trim().equals("")) {
368                                 System.out.println("No seviceInstanceId element in injectedVariables");
369                                 variables.put("mso-service-instance-id", UUID.randomUUID().toString());
370                         }
371
372                 } else {
373                         msoRequestId = getXMLTextElement(request, "request-id");
374
375                         if (msoRequestId == null) {
376                                 //check in injected variables
377                                 try {
378                                         msoRequestId = (String) injectedVariables.get("requestId");
379                                 }
380                                 catch(Exception e) {
381                                 }
382                                 if (msoRequestId == null || msoRequestId.trim().equals("")) {
383                                         String msg = "No request-id element in " + request;
384                                         System.out.println(msg);
385                                         fail(msg);
386                                 }
387                         }
388
389                         variables.put("mso-request-id", msoRequestId);
390
391                         // Note: some request types don't have a service-instance-id
392                         msoServiceInstanceId = getXMLTextElement(request, "service-instance-id");
393
394                         if (msoServiceInstanceId != null) {
395                                 variables.put("mso-service-instance-id", msoServiceInstanceId);
396                         }
397                 }
398
399                 return variables;
400         }
401
402         /**
403          * Private helper method that creates a camunda VariableMapImpl from a simple
404          * variable map.
405          * @param variables the simple variable map
406          * @return a VariableMap
407          */
408         private VariableMapImpl createVariableMapImpl(Map<String, Object> variables) {
409                 Map<String, Object> wrappedVariables = new HashMap<String, Object>();
410
411                 for (String key : variables.keySet()) {
412                         Object value = variables.get(key);
413                         wrappedVariables.put(key, wrapVariableValue(value));
414                 }
415
416                 VariableMapImpl variableMapImpl = new VariableMapImpl();
417                 variableMapImpl.put("variables", wrappedVariables);
418                 return variableMapImpl;
419         }
420
421         /**
422          * Private helper method that wraps a variable value for inclusion in a
423          * camunda VariableMapImpl.
424          * @param value the variable value
425          * @return the wrapped variable
426          */
427         private Map<String, Object> wrapVariableValue(Object value) {
428                 HashMap<String, Object> valueMap = new HashMap<String, Object>();
429                 valueMap.put("value", value);
430                 return valueMap;
431         }
432
433         /**
434          * Receives a response from an asynchronous process.
435          * Errors are handled with junit assertions and will cause the test to fail.
436          * @param businessKey the process business key
437          * @param asyncResponse the TestAsyncResponse object associated with the test
438          * @param timeout the timeout in milliseconds
439          * @return the WorkflowResponse
440          */
441         public WorkflowResponse receiveResponse(String businessKey,
442                         TestAsyncResponse asyncResponse, long timeout) {
443                 System.out.println("Waiting " + timeout + "ms for process with business key " + businessKey
444                         + " to send a response");
445
446                 long now = System.currentTimeMillis() + timeout;
447                 long endTime = now + timeout;
448
449                 while (now <= endTime) {
450                         Response response = asyncResponse.getResponse();
451
452                         if (response != null) {
453                                 System.out.println("Received a response from process with business key " + businessKey);
454
455                                 Object entity = response.getEntity();
456
457                                 if (!(entity instanceof WorkflowResponse)) {
458                                         String msg = "Response entity is " +
459                                                 (entity == null ? "null" : entity.getClass().getName()) +
460                                                 ", expected WorkflowResponse";
461                                         System.out.println(msg);
462                                         fail(msg);
463                                         return null; // unreachable
464                                 }
465
466                                 return (WorkflowResponse) entity;
467                         }
468
469                         try {
470                                 Thread.sleep(200);
471                         } catch (InterruptedException e) {
472                                 String msg = "Interrupted waiting for a response from process with business key " +
473                                         businessKey;
474                                 System.out.println(msg);
475                                 fail(msg);
476                                 return null; // unreachable
477                         }
478
479                         now = System.currentTimeMillis();
480                 }
481
482                 String msg = "No response received from process with business key " + businessKey +
483                         " within " + timeout + "ms";
484                 System.out.println(msg);
485                 fail("Process with business key " + businessKey + " did not end within 10000ms");
486                 return null; // unreachable
487         }
488
489         /**
490          * Runs a program to inject SDNC callback data into the test environment.
491          * A program is essentially just a list of keys that identify callback data
492          * to be injected, in sequence. An example program:
493          * <pre>
494          *     reserve, assign, delete:ERR
495          * </pre>
496          * Errors are handled with junit assertions and will cause the test to fail.
497          * @param callbacks an object containing callback data for the program
498          * @param program the program to execute
499          */
500         protected void injectSDNCRestCallbacks(CallbackSet callbacks, String program) {
501
502                 String[] cmds = program.replaceAll("\\s+", "").split(",");
503
504                 for (String cmd : cmds) {
505                         String action = cmd;
506                         String modifier = "STD";
507
508                         if (cmd.contains(":")) {
509                                 String[] parts = cmd.split(":");
510                                 action = parts[0];
511                                 modifier = parts[1];
512                         }
513
514                         String content = null;
515
516                         if ("STD".equals(modifier)) {
517                                 content = callbacks.get(action);
518
519                                 if (content == null) {
520                                         String msg = "No callback defined for '" + action + "' SDNC request";
521                                         System.out.println(msg);
522                                         fail(msg);
523                                 }
524                         } else if ("ERR".equals(modifier)) {
525                                 content = "{\"SDNCServiceError\":{\"sdncRequestId\":\"((REQUEST-ID))\",\"responseCode\":\"500\",\"responseMessage\":\"SIMULATED ERROR FROM SDNC ADAPTER\",\"ackFinalIndicator\":\"Y\"}}";
526                         } else {
527                                 String msg = "Invalid SDNC program modifier: '" + modifier + "'";
528                                 System.out.println(msg);
529                                 fail(msg);
530                         }
531
532                         if (!injectSDNCRestCallback(content, 10000)) {
533                                 fail("Failed to inject SDNC '" + action + "' callback");
534                         }
535
536                         try {
537                                 Thread.sleep(1000);
538                         } catch (InterruptedException e) {
539                                 fail("Interrupted after injection of SDNC '" + action + "' callback");
540                         }
541                 }
542         }
543
544         /**
545          * Runs a program to inject SDNC events into the test environment.
546          * A program is essentially just a list of keys that identify event data
547          * to be injected, in sequence. An example program:
548          * <pre>
549          *     event1, event2
550          * </pre>
551          * Errors are handled with junit assertions and will cause the test to fail.
552          * Defaults the Event Type to "SDNCAEvent" for backward compatibility.
553          * @param callbacks an object containing event data for the program
554          * @param program the program to execute
555          */
556         protected void injectSDNCEvents(CallbackSet callbacks, String program) {
557                 injectSDNCEvents(callbacks, program, "SDNCAEvent");
558         }
559
560         /**
561          * Runs a program to inject SDNC events into the test environment.
562          * A program is essentially just a list of keys that identify event data
563          * to be injected, in sequence. An example program:
564          * <pre>
565          *     event1, event2
566          * </pre>
567          * Errors are handled with junit assertions and will cause the test to fail.
568          * @param callbacks an object containing event data for the program
569          * @param program the program to execute
570          * @param eventType (i.e. "SDNCAEvent", "SNIROResponse", etc.)
571          */
572         protected void injectSDNCEvents(CallbackSet callbacks, String program, String eventType) {
573
574                 String[] cmds = program.replaceAll("\\s+", "").split(",");
575
576                 for (String cmd : cmds) {
577                         String action = cmd;
578                         String modifier = "STD";
579
580                         if (cmd.contains(":")) {
581                                 String[] parts = cmd.split(":");
582                                 action = parts[0];
583                                 modifier = parts[1];
584                         }
585
586                         String content = null;
587
588                         if ("STD".equals(modifier)) {
589                                 content = callbacks.get(action);
590
591                                 if (content == null) {
592                                         String msg = "No SDNC event callback defined for '" + action + "'";
593                                         System.out.println(msg);
594                                         fail(msg);
595                                 }
596                         } else {
597                                 String msg = "Invalid SDNC program modifier: '" + modifier + "'";
598                                 System.out.println(msg);
599                                 fail(msg);
600                         }
601
602                         if (!injectWorkflowMessage(eventType, content, 10000)) {
603                                 fail("Failed to inject SDNC '" + action + "' event");
604                         }
605
606                         try {
607                                 Thread.sleep(1000);
608                         } catch (InterruptedException e) {
609                                 fail("Interrupted after injection of SDNC '" + action + "' event");
610                         }
611                 }
612         }
613
614         /**
615          * Runs a program to inject SDNC callback data into the test environment.
616          * A program is essentially just a list of keys that identify callback data
617          * to be injected, in sequence. An example program:
618          * <pre>
619          *     reserve, assign, delete:ERR
620          * </pre>
621          * Errors are handled with junit assertions and will cause the test to fail.
622          * @param callbacks an object containing callback data for the program
623          * @param program the program to execute
624          */
625         public void injectSDNCCallbacks(CallbackSet callbacks, String program) {
626
627                 String[] cmds = program.replaceAll("\\s+", "").split(",");
628
629                 for (String cmd : cmds) {
630                         String action = cmd;
631                         String modifier = "STD";
632
633                         if (cmd.contains(":")) {
634                                 String[] parts = cmd.split(":");
635                                 action = parts[0];
636                                 modifier = parts[1];
637                         }
638
639                         String content = null;
640                         int respCode = 200;
641                         String respMsg = "OK";
642
643                         if ("STD".equals(modifier)) {
644                                 content = callbacks.get(action);
645
646                                 if (content == null) {
647                                         String msg = "No callback defined for '" + action + "' SDNC request";
648                                         System.out.println(msg);
649                                         fail(msg);
650                                 }
651
652                                 respCode = 200;
653                                 respMsg = "OK";
654                         } else if ("ERR".equals(modifier)) {
655                                 content = "<svc-request-id>((REQUEST-ID))</svc-request-id><response-code>500</response-code><response-message>SIMULATED ERROR FROM SDNC ADAPTER</response-message>";
656                                 respCode = 500;
657                                 respMsg = "SERVER ERROR";
658                         } else {
659                                 String msg = "Invalid SDNC program modifier: '" + modifier + "'";
660                                 System.out.println(msg);
661                                 fail(msg);
662                         }
663
664                         if (!injectSDNCCallback(respCode, respMsg, content, 10000)) {
665                                 fail("Failed to inject SDNC '" + action + "' callback");
666                         }
667
668                         try {
669                                 Thread.sleep(1000);
670                         } catch (InterruptedException e) {
671                                 fail("Interrupted after injection of SDNC '" + action + "' callback");
672                         }
673                 }
674         }
675
676         /**
677          * Runs a program to inject VNF adapter REST callback data into the test environment.
678          * A program is essentially just a list of keys that identify callback data
679          * to be injected, in sequence. An example program:
680          * <pre>
681          *     create, rollback
682          * </pre>
683          * Errors are handled with junit assertions and will cause the test to fail.
684          * @param callbacks an object containing callback data for the program
685          * @param program the program to execute
686          */
687         public void injectVNFRestCallbacks(CallbackSet callbacks, String program) {
688
689                 String[] cmds = program.replaceAll("\\s+", "").split(",");
690
691                 for (String cmd : cmds) {
692                         String action = cmd;
693                         String modifier = "STD";
694
695                         if (cmd.contains(":")) {
696                                 String[] parts = cmd.split(":");
697                                 action = parts[0];
698                                 modifier = parts[1];
699                         }
700
701                         String content = null;
702
703                         if ("STD".equals(modifier)) {
704                                 content = callbacks.get(action);
705
706                                 if (content == null) {
707                                         String msg = "No callback defined for '" + action + "' VNF REST request";
708                                         System.out.println(msg);
709                                         fail(msg);
710                                 }
711                         } else if ("ERR".equals(modifier)) {
712                                 content = "SIMULATED ERROR FROM VNF ADAPTER";
713                         } else {
714                                 String msg = "Invalid VNF REST program modifier: '" + modifier + "'";
715                                 System.out.println(msg);
716                                 fail(msg);
717                         }
718
719                         if (!injectVnfAdapterRestCallback(content, 10000)) {
720                                 fail("Failed to inject VNF REST '" + action + "' callback");
721                         }
722
723                         try {
724                                 Thread.sleep(1000);
725                         } catch (InterruptedException e) {
726                                 fail("Interrupted after injection of VNF REST '" + action + "' callback");
727                         }
728                 }
729         }
730
731         /**
732          * Runs a program to inject VNF callback data into the test environment.
733          * A program is essentially just a list of keys that identify callback data
734          * to be injected, in sequence. An example program:
735          * <pre>
736          *     createVnf, deleteVnf
737          * </pre>
738          * Errors are handled with junit assertions and will cause the test to fail.
739          * @param callbacks an object containing callback data for the program
740          * @param program the program to execute
741          */
742         protected void injectVNFCallbacks(CallbackSet callbacks, String program) {
743
744                 String[] cmds = program.replaceAll("\\s+", "").split(",");
745
746                 for (String cmd : cmds) {
747                         String action = cmd;
748                         String modifier = "STD";
749
750                         if (cmd.contains(":")) {
751                                 String[] parts = cmd.split(":");
752                                 action = parts[0];
753                                 modifier = parts[1];
754                         }
755
756                         String content = null;
757
758                         if ("STD".equals(modifier)) {
759                                 content = callbacks.get(action);
760
761                                 if (content == null) {
762                                         String msg = "No callback defined for '" + action + "' VNF request";
763                                         System.out.println(msg);
764                                         fail(msg);
765                                 }
766
767                         } else if ("ERR".equals(modifier)) {
768                                 String msg = "Currently unsupported VNF program modifier: '" + modifier + "'";
769                                 System.out.println(msg);
770                                 fail(msg);
771                         } else {
772                                 String msg = "Invalid VNF program modifier: '" + modifier + "'";
773                                 System.out.println(msg);
774                                 fail(msg);
775                         }
776
777                         boolean injected = false;
778
779                         if (content.contains("createVnfNotification")) {
780                                 injected = injectCreateVNFCallback(content, 10000);
781                         } else if (content.contains("deleteVnfNotification")) {
782                                 injected = injectDeleteVNFCallback(content, 10000);
783                         } else if (content.contains("updateVnfNotification")) {
784                                 injected = injectUpdateVNFCallback(content, 10000);
785                         }
786
787                         if (!injected) {
788                                 String msg = "Failed to inject VNF '" + action + "' callback";
789                                 System.out.println(msg);
790                                 fail(msg);
791                         }
792
793                         try {
794                                 Thread.sleep(1000);
795                         } catch (InterruptedException e) {
796                                 fail("Interrupted after injection of VNF '" + action + "' callback");
797                         }
798                 }
799         }
800
801         /**
802          * Waits for the number of running processes with the specified process
803          * definition key to equal a particular count.
804          * @param processKey the process definition key
805          * @param count the desired count
806          * @param timeout the timeout in milliseconds
807          */
808         public void waitForRunningProcessCount(String processKey, int count, long timeout) {
809                 System.out.println("Waiting " + timeout + "ms for there to be " + count + " "
810                         + processKey + " instances");
811
812                 long now = System.currentTimeMillis() + timeout;
813                 long endTime = now + timeout;
814                 int last = -1;
815
816                 while (now <= endTime) {
817                         int actual = processEngineRule.getRuntimeService()
818                                 .createProcessInstanceQuery()
819                                 .processDefinitionKey(processKey)
820                                 .list().size();
821
822                         if (actual != last) {
823                                 System.out.println("There are now " + actual + " "
824                                         + processKey + " instances");
825                                 last = actual;
826                         }
827
828                         if (actual == count) {
829                                 return;
830                         }
831
832                         try {
833                                 Thread.sleep(200);
834                         } catch (InterruptedException e) {
835                                 String msg = "Interrupted waiting for there to be " + count + " "
836                                         + processKey + " instances";
837                                 System.out.println(msg);
838                                 fail(msg);
839                         }
840
841                         now = System.currentTimeMillis();
842                 }
843
844                 String msg = "Timed out waiting for there to be " + count + " "
845                         + processKey + " instances";
846                 System.out.println(msg);
847                 fail(msg);
848         }
849
850         /**
851          * Waits for the specified process variable to be set.
852          * @param processKey the process definition key
853          * @param variable the variable name
854          * @param timeout the timeout in milliseconds
855          * @return the variable value, or null if it cannot be obtained
856          *         in the specified time
857          */
858         protected Object getProcessVariable(String processKey, String variable,
859                         long timeout) {
860
861                 System.out.println("Waiting " + timeout + "ms for "
862                         + processKey + "." + variable + " to be set");
863
864                 long now = System.currentTimeMillis() + timeout;
865                 long endTime = now + timeout;
866
867                 ProcessInstance processInstance = null;
868                 Object value = null;
869
870                 while (value == null) {
871                         if (now > endTime) {
872                                 if (processInstance == null) {
873                                         System.out.println("Timed out waiting for "
874                                                 + processKey + " to start");
875                                 } else {
876                                         System.out.println("Timed out waiting for "
877                                                 + processKey + "[" + processInstance.getId()
878                                                 + "]." + variable + " to be set");
879                                 }
880
881                                 return null;
882                         }
883
884                         if (processInstance == null) {
885                                 processInstance = processEngineRule.getRuntimeService()
886                                         .createProcessInstanceQuery()
887                                         .processDefinitionKey(processKey)
888                                         .singleResult();
889                         }
890
891                         if (processInstance != null) {
892                                 value = processEngineRule.getRuntimeService()
893                                         .getVariable(processInstance.getId(), variable);
894                         }
895
896                         try {
897                                 Thread.sleep(200);
898                         } catch (InterruptedException e) {
899                                 System.out.println("Interrupted waiting for "
900                                         + processKey + "." + variable + " to be set");
901                                 return null;
902                         }
903
904                         now = System.currentTimeMillis();
905                 }
906
907                 System.out.println(processKey + "["
908                         + processInstance.getId() + "]." + variable + "="
909                         + value);
910
911                 return value;
912         }
913
914         /**
915          * Injects a single SDNC adapter callback request. The specified callback data
916          * may contain the placeholder string ((REQUEST-ID)) which is replaced with
917          * the actual SDNC request ID. Note: this is not the requestId in the original
918          * MSO request.
919          * @param content the content of the callback
920          * @param timeout the timeout in milliseconds
921          * @return true if the callback could be injected, false otherwise
922          */
923         protected boolean injectSDNCRestCallback(String content, long timeout) {
924                 String sdncRequestId = (String) getProcessVariable("SDNCAdapterRestV1",
925                         "SDNCAResponse_CORRELATOR", timeout);
926
927                 if (sdncRequestId == null) {
928                         return false;
929                 }
930
931                 content = content.replace("((REQUEST-ID))", sdncRequestId);
932                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
933                 content = content.replace("{{REQUEST-ID}}", sdncRequestId);
934
935                 System.out.println("Injecting SDNC adapter callback");
936                 WorkflowMessageResource workflowMessageResource = new WorkflowMessageResource();
937                 workflowMessageResource.setProcessEngineServices4junit(processEngineRule);
938                 Response response = workflowMessageResource.deliver("SDNCAResponse", sdncRequestId, content);
939                 System.out.println("Workflow response to SDNC adapter callback: " + response);
940                 return true;
941         }
942
943         /**
944          * Injects a single SDNC adapter callback request. The specified callback data
945          * may contain the placeholder string ((REQUEST-ID)) which is replaced with
946          * the actual SDNC request ID. Note: this is not the requestId in the original
947          * MSO request.
948          * @param content the content of the callback
949          * @param respCode the response code (normally 200)
950          * @param respMsg the response message (normally "OK")
951          * @param timeout the timeout in milliseconds
952          * @return true if the callback could be injected, false otherwise
953          */
954         protected boolean injectSDNCCallback(int respCode, String respMsg,
955                         String content, long timeout) {
956
957                 String sdncRequestId = (String) getProcessVariable("sdncAdapter",
958                         "SDNCA_requestId", timeout);
959
960                 if (sdncRequestId == null) {
961                         return false;
962                 }
963
964                 content = content.replace("((REQUEST-ID))", sdncRequestId);
965                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
966                 content = content.replace("{{REQUEST-ID}}", sdncRequestId);
967
968                 System.out.println("Injecting SDNC adapter callback");
969                 CallbackHeader callbackHeader = new CallbackHeader();
970                 callbackHeader.setRequestId(sdncRequestId);
971                 callbackHeader.setResponseCode(String.valueOf(respCode));
972                 callbackHeader.setResponseMessage(respMsg);
973                 SDNCAdapterCallbackRequest sdncAdapterCallbackRequest = new SDNCAdapterCallbackRequest();
974                 sdncAdapterCallbackRequest.setCallbackHeader(callbackHeader);
975                 sdncAdapterCallbackRequest.setRequestData(content);
976                 SDNCAdapterCallbackServiceImpl callbackService = new SDNCAdapterCallbackServiceImpl();
977                 callbackService.setProcessEngineServices4junit(processEngineRule);
978                 SDNCAdapterResponse sdncAdapterResponse = callbackService.sdncAdapterCallback(sdncAdapterCallbackRequest);
979                 System.out.println("Workflow response to SDNC adapter callback: " + sdncAdapterResponse);
980
981                 return true;
982         }
983
984         /**
985          * Injects a single VNF adapter callback request. The specified callback data
986          * may contain the placeholder string ((MESSAGE-ID)) which is replaced with
987          * the actual message ID. Note: this is not the requestId in the original
988          * MSO request.
989          * @param content the content of the callback
990          * @param timeout the timeout in milliseconds
991          * @return true if the callback could be injected, false otherwise
992          */
993         protected boolean injectVnfAdapterRestCallback(String content, long timeout) {
994                 String messageId = (String) getProcessVariable("vnfAdapterRestV1",
995                         "VNFAResponse_CORRELATOR", timeout);
996
997                 if (messageId == null) {
998                         return false;
999                 }
1000
1001                 content = content.replace("((MESSAGE-ID))", messageId);
1002                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
1003                 content = content.replace("{{MESSAGE-ID}}", messageId);
1004
1005                 System.out.println("Injecting VNF adapter callback");
1006                 WorkflowMessageResource workflowMessageResource = new WorkflowMessageResource();
1007                 workflowMessageResource.setProcessEngineServices4junit(processEngineRule);
1008                 Response response = workflowMessageResource.deliver("VNFAResponse", messageId, content);
1009                 System.out.println("Workflow response to VNF adapter callback: " + response);
1010                 return true;
1011         }
1012
1013         /**
1014          * Injects a Create VNF adapter callback request. The specified callback data
1015          * may contain the placeholder string ((MESSAGE-ID)) which is replaced with
1016          * the actual message ID.  It may also contain the placeholder string
1017          * ((REQUEST-ID)) which is replaced request ID of the original MSO request.
1018          * @param content the content of the callback
1019          * @param timeout the timeout in milliseconds
1020          * @return true if the callback could be injected, false otherwise
1021          * @throws JAXBException if the content does not adhere to the schema
1022          */
1023         protected boolean injectCreateVNFCallback(String content, long timeout) {
1024
1025                 String messageId = (String) getProcessVariable("vnfAdapterCreateV1",
1026                         "VNFC_messageId", timeout);
1027
1028                 if (messageId == null) {
1029                         return false;
1030                 }
1031
1032                 content = content.replace("((MESSAGE-ID))", messageId);
1033                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
1034                 content = content.replace("{{MESSAGE-ID}}", messageId);
1035
1036                 if(content.contains("((REQUEST-ID))")){
1037                         content = content.replace("((REQUEST-ID))", msoRequestId);
1038                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
1039                         content = content.replace("{{REQUEST-ID}}", msoRequestId);
1040                 }
1041
1042                 System.out.println("Injecting VNF adapter callback");
1043
1044                 // Is it possible to unmarshal this with JAXB?  I couldn't.
1045
1046                 CreateVnfNotification createVnfNotification = new CreateVnfNotification();
1047                 XPathTool xpathTool = new VnfNotifyXPathTool();
1048                 xpathTool.setXML(content);
1049
1050                 try {
1051                         String completed = xpathTool.evaluate(
1052                                 "/tns:createVnfNotification/tns:completed/text()");
1053                         createVnfNotification.setCompleted("true".equals(completed));
1054
1055                         String vnfId = xpathTool.evaluate(
1056                                 "/tns:createVnfNotification/tns:vnfId/text()");
1057                         createVnfNotification.setVnfId(vnfId);
1058
1059                         NodeList entries = (NodeList) xpathTool.evaluate(
1060                                 "/tns:createVnfNotification/tns:outputs/tns:entry",
1061                                 XPathConstants.NODESET);
1062
1063                         CreateVnfNotificationOutputs outputs = new CreateVnfNotificationOutputs();
1064
1065                         for (int i = 0; i < entries.getLength(); i++) {
1066                                 Node node = entries.item(i);
1067
1068                                 if (node.getNodeType() == Node.ELEMENT_NODE) {
1069                                         Element entry = (Element) node;
1070                                         String key = entry.getElementsByTagNameNS("*", "key").item(0).getTextContent();
1071                                         String value = entry.getElementsByTagNameNS("*", "value").item(0).getTextContent();
1072                                         outputs.add(key, value);
1073                                 }
1074                         }
1075
1076                         createVnfNotification.setOutputs(outputs);
1077
1078                         VnfRollback rollback = new VnfRollback();
1079
1080                         String cloudSiteId = xpathTool.evaluate(
1081                                 "/tns:createVnfNotification/tns:rollback/tns:cloudSiteId/text()");
1082                         rollback.setCloudSiteId(cloudSiteId);
1083
1084                         String requestId = xpathTool.evaluate(
1085                                 "/tns:createVnfNotification/tns:rollback/tns:msoRequest/tns:requestId/text()");
1086                         String serviceInstanceId = xpathTool.evaluate(
1087                                 "/tns:createVnfNotification/tns:rollback/tns:msoRequest/tns:serviceInstanceId/text()");
1088
1089                         if (requestId != null || serviceInstanceId != null) {
1090                                 MsoRequest msoRequest = new MsoRequest();
1091                                 msoRequest.setRequestId(requestId);
1092                                 msoRequest.setServiceInstanceId(serviceInstanceId);
1093                                 rollback.setMsoRequest(msoRequest);
1094                         }
1095
1096                         String tenantCreated = xpathTool.evaluate(
1097                                 "/tns:createVnfNotification/tns:rollback/tns:tenantCreated/text()");
1098                         rollback.setTenantCreated("true".equals(tenantCreated));
1099
1100                         String tenantId = xpathTool.evaluate(
1101                                 "/tns:createVnfNotification/tns:rollback/tns:tenantId/text()");
1102                         rollback.setTenantId(tenantId);
1103
1104                         String vnfCreated = xpathTool.evaluate(
1105                                 "/tns:createVnfNotification/tns:rollback/tns:vnfCreated/text()");
1106                         rollback.setVnfCreated("true".equals(vnfCreated));
1107
1108                         String rollbackVnfId = xpathTool.evaluate(
1109                                 "/tns:createVnfNotification/tns:rollback/tns:vnfId/text()");
1110                         rollback.setVnfId(rollbackVnfId);
1111
1112                         createVnfNotification.setRollback(rollback);
1113
1114                 } catch (Exception e) {
1115                         System.out.println("Failed to unmarshal VNF callback content:");
1116                         System.out.println(content);
1117                         return false;
1118                 }
1119
1120                 VnfAdapterNotifyServiceImpl notifyService = new VnfAdapterNotifyServiceImpl();
1121                 notifyService.setProcessEngineServices4junit(processEngineRule);
1122
1123                 notifyService.createVnfNotification(
1124                         messageId,
1125                         createVnfNotification.isCompleted(),
1126                         createVnfNotification.getException(),
1127                         createVnfNotification.getErrorMessage(),
1128                         createVnfNotification.getVnfId(),
1129                         createVnfNotification.getOutputs(),
1130                         createVnfNotification.getRollback());
1131
1132                 return true;
1133         }
1134
1135         /**
1136          * Injects a Delete VNF adapter callback request. The specified callback data
1137          * may contain the placeholder string ((MESSAGE-ID)) which is replaced with
1138          * the actual message ID.  It may also contain the placeholder string
1139          * ((REQUEST-ID)) which is replaced request ID of the original MSO request.
1140          * @param content the content of the callback
1141          * @param timeout the timeout in milliseconds
1142          * @return true if the callback could be injected, false otherwise
1143          * @throws JAXBException if the content does not adhere to the schema
1144          */
1145         protected boolean injectDeleteVNFCallback(String content, long timeout) {
1146
1147                 String messageId = (String) getProcessVariable("vnfAdapterDeleteV1",
1148                         "VNFDEL_uuid", timeout);
1149
1150                 if (messageId == null) {
1151                         return false;
1152                 }
1153
1154                 content = content.replace("((MESSAGE-ID))", messageId);
1155                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
1156                 content = content.replace("{{MESSAGE-ID}}", messageId);
1157
1158                 System.out.println("Injecting VNF adapter delete callback");
1159
1160                 // Is it possible to unmarshal this with JAXB?  I couldn't.
1161
1162                 DeleteVnfNotification deleteVnfNotification = new DeleteVnfNotification();
1163                 XPathTool xpathTool = new VnfNotifyXPathTool();
1164                 xpathTool.setXML(content);
1165
1166                 try {
1167                         String completed = xpathTool.evaluate(
1168                                 "/tns:deleteVnfNotification/tns:completed/text()");
1169                         deleteVnfNotification.setCompleted("true".equals(completed));
1170                         // if notification failure, set the exception and error message
1171                         if (deleteVnfNotification.isCompleted() == false) {
1172                                 deleteVnfNotification.setException(MsoExceptionCategory.INTERNAL);
1173                                 deleteVnfNotification.setErrorMessage(xpathTool.evaluate(
1174                                                 "/tns:deleteVnfNotification/tns:errorMessage/text()")) ;
1175                         }
1176
1177                 } catch (Exception e) {
1178                         System.out.println("Failed to unmarshal VNF Delete callback content:");
1179                         System.out.println(content);
1180                         return false;
1181                 }
1182
1183                 VnfAdapterNotifyServiceImpl notifyService = new VnfAdapterNotifyServiceImpl();
1184                 notifyService.setProcessEngineServices4junit(processEngineRule);
1185
1186                 notifyService.deleteVnfNotification(
1187                         messageId,
1188                         deleteVnfNotification.isCompleted(),
1189                         deleteVnfNotification.getException(),
1190                         deleteVnfNotification.getErrorMessage());
1191
1192                 return true;
1193         }
1194
1195         /**
1196          * Injects a Update VNF adapter callback request. The specified callback data
1197          * may contain the placeholder string ((MESSAGE-ID)) which is replaced with
1198          * the actual message ID.  It may also contain the placeholder string
1199          * ((REQUEST-ID)) which is replaced request ID of the original MSO request.
1200          * @param content the content of the callback
1201          * @param timeout the timeout in milliseconds
1202          * @return true if the callback could be injected, false otherwise
1203          * @throws JAXBException if the content does not adhere to the schema
1204          */
1205         protected boolean injectUpdateVNFCallback(String content, long timeout) {
1206
1207                 String messageId = (String) getProcessVariable("vnfAdapterUpdate",
1208                         "VNFU_messageId", timeout);
1209
1210                 if (messageId == null) {
1211                         return false;
1212                 }
1213
1214                 content = content.replace("((MESSAGE-ID))", messageId);
1215                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
1216                 content = content.replace("{{MESSAGE-ID}}", messageId);
1217
1218                 content = content.replace("((REQUEST-ID))", msoRequestId);
1219                 // Deprecated usage.  All test code should switch to the (( ... )) syntax.
1220                 content = content.replace("{{REQUEST-ID}}", msoRequestId);
1221
1222                 System.out.println("Injecting VNF adapter callback");
1223
1224                 // Is it possible to unmarshal this with JAXB?  I couldn't.
1225
1226                 UpdateVnfNotification updateVnfNotification = new UpdateVnfNotification();
1227                 XPathTool xpathTool = new VnfNotifyXPathTool();
1228                 xpathTool.setXML(content);
1229
1230                 try {
1231                         String completed = xpathTool.evaluate(
1232                                 "/tns:updateVnfNotification/tns:completed/text()");
1233                         updateVnfNotification.setCompleted("true".equals(completed));
1234
1235                         NodeList entries = (NodeList) xpathTool.evaluate(
1236                                 "/tns:updateVnfNotification/tns:outputs/tns:entry",
1237                                 XPathConstants.NODESET);
1238
1239                         UpdateVnfNotificationOutputs outputs = new UpdateVnfNotificationOutputs();
1240
1241                         for (int i = 0; i < entries.getLength(); i++) {
1242                                 Node node = entries.item(i);
1243
1244                                 if (node.getNodeType() == Node.ELEMENT_NODE) {
1245                                         Element entry = (Element) node;
1246                                         String key = entry.getElementsByTagNameNS("*", "key").item(0).getTextContent();
1247                                         String value = entry.getElementsByTagNameNS("*", "value").item(0).getTextContent();
1248                                         outputs.add(key, value);
1249                                 }
1250                         }
1251
1252                         updateVnfNotification.setOutputs(outputs);
1253
1254                         VnfRollback rollback = new VnfRollback();
1255
1256                         String cloudSiteId = xpathTool.evaluate(
1257                                 "/tns:updateVnfNotification/tns:rollback/tns:cloudSiteId/text()");
1258                         rollback.setCloudSiteId(cloudSiteId);
1259
1260                         String requestId = xpathTool.evaluate(
1261                                 "/tns:updateVnfNotification/tns:rollback/tns:msoRequest/tns:requestId/text()");
1262                         String serviceInstanceId = xpathTool.evaluate(
1263                                 "/tns:updateVnfNotification/tns:rollback/tns:msoRequest/tns:serviceInstanceId/text()");
1264
1265                         if (requestId != null || serviceInstanceId != null) {
1266                                 MsoRequest msoRequest = new MsoRequest();
1267                                 msoRequest.setRequestId(requestId);
1268                                 msoRequest.setServiceInstanceId(serviceInstanceId);
1269                                 rollback.setMsoRequest(msoRequest);
1270                         }
1271
1272                         String tenantCreated = xpathTool.evaluate(
1273                                 "/tns:updateVnfNotification/tns:rollback/tns:tenantCreated/text()");
1274                         rollback.setTenantCreated("true".equals(tenantCreated));
1275
1276                         String tenantId = xpathTool.evaluate(
1277                                 "/tns:updateVnfNotification/tns:rollback/tns:tenantId/text()");
1278                         rollback.setTenantId(tenantId);
1279
1280                         String vnfCreated = xpathTool.evaluate(
1281                                 "/tns:updateVnfNotification/tns:rollback/tns:vnfCreated/text()");
1282                         rollback.setVnfCreated("true".equals(vnfCreated));
1283
1284                         String rollbackVnfId = xpathTool.evaluate(
1285                                 "/tns:updateVnfNotification/tns:rollback/tns:vnfId/text()");
1286                         rollback.setVnfId(rollbackVnfId);
1287
1288                         updateVnfNotification.setRollback(rollback);
1289
1290                 } catch (Exception e) {
1291                         System.out.println("Failed to unmarshal VNF callback content:");
1292                         System.out.println(content);
1293                         return false;
1294                 }
1295
1296                 VnfAdapterNotifyServiceImpl notifyService = new VnfAdapterNotifyServiceImpl();
1297                 notifyService.setProcessEngineServices4junit(processEngineRule);
1298
1299                 notifyService.updateVnfNotification(
1300                         messageId,
1301                         updateVnfNotification.isCompleted(),
1302                         updateVnfNotification.getException(),
1303                         updateVnfNotification.getErrorMessage(),
1304                         updateVnfNotification.getOutputs(),
1305                         updateVnfNotification.getRollback());
1306
1307                 return true;
1308         }
1309
1310         /**
1311          * Injects a workflow message. The specified callback data may contain the
1312          * placeholder string ((CORRELATOR)) which is replaced with the actual
1313          * correlator value.
1314          * @param content the message type
1315          * @param content the message content
1316          * @param timeout the timeout in milliseconds
1317          * @return true if the event could be injected, false otherwise
1318          */
1319         protected boolean injectWorkflowMessage(String messageType, String content, long timeout) {
1320                 String correlator = (String) getProcessVariable("ReceiveWorkflowMessage",
1321                         messageType + "_CORRELATOR", timeout);
1322
1323                 if (correlator == null) {
1324                         return false;
1325                 }
1326
1327                 content = content.replace("((CORRELATOR))", correlator);
1328
1329                 System.out.println("Injecting " + messageType + " message");
1330                 WorkflowMessageResource workflowMessageResource = new WorkflowMessageResource();
1331                 workflowMessageResource.setProcessEngineServices4junit(processEngineRule);
1332                 Response response = workflowMessageResource.deliver(messageType, correlator, content);
1333                 System.out.println("Workflow response to " + messageType + " message: " + response);
1334                 return true;
1335         }
1336
1337         /**
1338          * Wait for the process to end.
1339          * @param businessKey the process business key
1340          * @param timeout the amount of time to wait, in milliseconds
1341          */
1342         public void waitForProcessEnd(String businessKey, long timeout) {
1343                 System.out.println("Waiting " + timeout + "ms for process with business key " +
1344                         businessKey + " to end");
1345
1346                 long now = System.currentTimeMillis() + timeout;
1347                 long endTime = now + timeout;
1348
1349                 while (now <= endTime) {
1350                         if (isProcessEnded(businessKey)) {
1351                                 System.out.println("Process with business key " + businessKey + " has ended");
1352                                 return;
1353                         }
1354
1355                         try {
1356                                 Thread.sleep(200);
1357                         } catch (InterruptedException e) {
1358                                 String msg = "Interrupted waiting for process with business key " +
1359                                         businessKey + " to end";
1360                                 System.out.println(msg);
1361                                 fail(msg);
1362                         }
1363
1364                         now = System.currentTimeMillis();
1365                 }
1366
1367                 String msg = "Process with business key " + businessKey +
1368                         " did not end within " + timeout + "ms";
1369                 System.out.println(msg);
1370                 fail(msg);
1371         }
1372
1373         /**
1374          * Verifies that the specified historic process variable has the specified value.
1375          * If the variable does not have the specified value, the test is failed.
1376          * @param businessKey the process business key
1377          * @param variable the variable name
1378          * @param value the expected variable value
1379          */
1380         public void checkVariable(String businessKey, String variable, Object value) {
1381                 if (!isProcessEnded(businessKey)) {
1382                         fail("Cannot get historic variable " + variable + " because process with business key " +
1383                                 businessKey + " has not ended");
1384                 }
1385
1386                 Object variableValue = getVariableFromHistory(businessKey, variable);
1387                 assertEquals(value, variableValue);
1388         }
1389
1390         /**
1391          * Checks to see if the specified process is ended.
1392          * @param businessKey the process business Key
1393          * @return true if the process is ended
1394          */
1395         protected boolean isProcessEnded(String businessKey) {
1396                 HistoricProcessInstance processInstance = processEngineRule.getHistoryService()
1397                         .createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).singleResult();
1398                 return processInstance != null && processInstance.getEndTime() != null;
1399         }
1400
1401         /**
1402          * Gets a variable value from a historical process instance.
1403          * @param businessKey the process business key
1404          * @param variableName the variable name
1405          * @return the variable value, or null if the variable could not be
1406          * obtained
1407          */
1408         public Object getVariableFromHistory(String businessKey, String variableName) {
1409                 try {
1410                         HistoricProcessInstance processInstance = processEngineRule.getHistoryService()
1411                                 .createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).singleResult();
1412
1413                         if (processInstance == null) {
1414                                 return null;
1415                         }
1416
1417                         HistoricVariableInstance v = processEngineRule.getHistoryService()
1418                                 .createHistoricVariableInstanceQuery().processInstanceId(processInstance.getId())
1419                                 .variableName(variableName).singleResult();
1420                         return v == null ? null : v.getValue();
1421                 } catch (Exception e) {
1422                         System.out.println("Error retrieving variable " + variableName +
1423                                 " from historical process with business key " + businessKey + ": " + e);
1424                         return null;
1425                 }
1426         }
1427
1428         /**
1429          * Gets the value of a subflow variable from the specified subflow's
1430          * historical process instance.
1431          *
1432          * @param subflowName - the name of the subflow that contains the variable
1433          * @param variableName the variable name
1434          *
1435          * @return the variable value, or null if the variable could not be obtained
1436          *
1437          */
1438         protected Object getVariableFromSubflowHistory(String subflowName, String variableName) {
1439                 try {
1440                         List<HistoricProcessInstance> processInstanceList = processEngineRule.getHistoryService()
1441                                         .createHistoricProcessInstanceQuery().processDefinitionName(subflowName).list();
1442
1443                         Collections.sort(processInstanceList, new Comparator<HistoricProcessInstance>() {
1444                             public int compare(HistoricProcessInstance m1, HistoricProcessInstance m2) {
1445                                 return m1.getStartTime().compareTo(m2.getStartTime());
1446                             }
1447                         });
1448
1449                         HistoricProcessInstance processInstance = processInstanceList.get(0);
1450
1451                         if (processInstanceList == null) {
1452                                 return null;
1453                         }
1454
1455                         HistoricVariableInstance v = processEngineRule.getHistoryService()
1456                                 .createHistoricVariableInstanceQuery().processInstanceId(processInstance.getId())
1457                                 .variableName(variableName).singleResult();
1458                         return v == null ? null : v.getValue();
1459                 } catch (Exception e) {
1460                         System.out.println("Error retrieving variable " + variableName +
1461                                         " from sub flow: " + subflowName + ", Exception is: " + e);
1462                         return null;
1463                 }
1464         }
1465
1466         /**
1467          * Gets the value of a subflow variable from the subflow's
1468          * historical process x instance.
1469          *
1470          * @param subflowName - the name of the subflow that contains the variable
1471          * @param variableName the variable name
1472          * @param subflowInstanceIndex - the instance of the subflow (use when same subflow is called more than once from mainflow)
1473          *
1474          * @return the variable value, or null if the variable could not be obtained
1475          */
1476         protected Object getVariableFromSubflowHistory(int subflowInstanceIndex, String subflowName, String variableName) {
1477                 try {
1478                         List<HistoricProcessInstance> processInstanceList = processEngineRule.getHistoryService()
1479                                         .createHistoricProcessInstanceQuery().processDefinitionName(subflowName).list();
1480
1481                         Collections.sort(processInstanceList, new Comparator<HistoricProcessInstance>() {
1482                             public int compare(HistoricProcessInstance m1, HistoricProcessInstance m2) {
1483                                 return m1.getStartTime().compareTo(m2.getStartTime());
1484                             }
1485                         });
1486
1487                         HistoricProcessInstance processInstance = processInstanceList.get(subflowInstanceIndex);
1488
1489                         if (processInstanceList == null) {
1490                                 return null;
1491                         }
1492
1493                         HistoricVariableInstance v = processEngineRule.getHistoryService()
1494                                 .createHistoricVariableInstanceQuery().processInstanceId(processInstance.getId())
1495                                 .variableName(variableName).singleResult();
1496                         return v == null ? null : v.getValue();
1497                 } catch (Exception e) {
1498                         System.out.println("Error retrieving variable " + variableName +
1499                                 " from " + subflowInstanceIndex + " instance index of sub flow: " + subflowName + ", Exception is: " + e);
1500                         return null;
1501                 }
1502         }
1503
1504
1505         /**
1506          * Extracts text from an XML element. This method is not namespace aware
1507          * (namespaces are ignored).  The first matching element is selected.
1508          * @param xml the XML document or fragment
1509          * @param tag the desired element, e.g. "<name>"
1510          * @return the element text, or null if the element was not found
1511          */
1512         protected String getXMLTextElement(String xml, String tag) {
1513                 xml = removeXMLNamespaces(xml);
1514
1515                 if (!tag.startsWith("<")) {
1516                         tag = "<" + tag + ">";
1517                 }
1518
1519                 int start = xml.indexOf(tag);
1520
1521                 if (start == -1) {
1522                         return null;
1523                 }
1524
1525                 int end = xml.indexOf('<', start + tag.length());
1526
1527                 if (end == -1) {
1528                         return null;
1529                 }
1530
1531                 return xml.substring(start + tag.length(), end);
1532         }
1533
1534         /**
1535          * Removes namespace definitions and prefixes from XML, if any.
1536          */
1537         private String removeXMLNamespaces(String xml) {
1538                 // remove xmlns declaration
1539                 xml = xml.replaceAll("xmlns.*?(\"|\').*?(\"|\')", "");
1540
1541                 // remove opening tag prefix
1542                 xml = xml.replaceAll("(<)(\\w+:)(.*?>)", "$1$3");
1543
1544                 // remove closing tags prefix
1545                 xml = xml.replaceAll("(</)(\\w+:)(.*?>)", "$1$3");
1546
1547                 // remove extra spaces left when xmlns declarations are removed
1548                 xml = xml.replaceAll("\\s+>", ">");
1549
1550                 return xml;
1551         }
1552
1553         /**
1554          * Asserts that two XML documents are semantically equivalent.  Differences
1555          * in whitespace or in namespace usage do not affect the comparison.
1556          * @param expected the expected XML
1557          * @param actual the XML to test
1558          * @throws SAXException
1559          * @throws IOException
1560          */
1561     public static void assertXMLEquals(String expected, String actual)
1562                 throws SAXException, IOException {
1563         XMLUnit.setIgnoreWhitespace(true);
1564         XMLUnit.setIgnoreAttributeOrder(true);
1565         DetailedDiff diff = new DetailedDiff(XMLUnit.compareXML(expected, actual));
1566         List<?> allDifferences = diff.getAllDifferences();
1567         assertEquals("Differences found: " + diff.toString(), 0, allDifferences.size());
1568     }
1569
1570         /**
1571          * A test implementation of AsynchronousResponse.
1572          */
1573         public class TestAsyncResponse implements AsynchronousResponse {
1574                 Response response = null;
1575
1576                 /**
1577                  * {@inheritDoc}
1578                  */
1579                 @Override
1580                 public synchronized void setResponse(Response response) {
1581                         this.response = response;
1582                 }
1583
1584                 /**
1585                  * Gets the response.
1586                  * @return the response, or null if none has been produced yet
1587                  */
1588                 public synchronized Response getResponse() {
1589                         return response;
1590                 }
1591         }
1592
1593         /**
1594          * An object that contains callback data for a "program".
1595          */
1596         public class CallbackSet {
1597                 private final Map<String, String> map = new HashMap<String, String>();
1598
1599                 /**
1600                  * Add callback data to the set.
1601                  * @param action the action with which the data is associated
1602                  * @param content the callback data
1603                  */
1604                 public void put(String action, String content) {
1605                         map.put(action, content);
1606                 }
1607
1608                 /**
1609                  * Retrieve callback data from the set.
1610                  * @param action the action with which the data is associated
1611                  * @return the callback data, or null if there is none for the specified operation
1612                  */
1613                 public String get(String action) {
1614                         return map.get(action);
1615                 }
1616         }
1617
1618         /**
1619          * A tool for evaluating XPath expressions.
1620          */
1621         protected class XPathTool {
1622                 private final DocumentBuilderFactory factory;
1623                 private final SimpleNamespaceContext context = new SimpleNamespaceContext();
1624                 private final XPath xpath = XPathFactory.newInstance().newXPath();
1625                 private String xml = null;
1626                 private Document doc = null;
1627
1628                 /**
1629                  * Constructor.
1630                  */
1631                 public XPathTool() {
1632                         factory = DocumentBuilderFactory.newInstance();
1633                         factory.setNamespaceAware(true);
1634                         xpath.setNamespaceContext(context);
1635                 }
1636
1637                 /**
1638                  * Adds a namespace.
1639                  * @param prefix the namespace prefix
1640                  * @param uri the namespace uri
1641                  */
1642                 public synchronized void addNamespace(String prefix, String uri) {
1643                         context.add(prefix, uri);
1644                 }
1645
1646                 /**
1647                  * Sets the XML content to be operated on.
1648                  * @param xml the XML content
1649                  */
1650                 public synchronized void setXML(String xml) {
1651                         this.xml = xml;
1652                         this.doc = null;
1653                 }
1654
1655                 /**
1656                  * Returns the document object.
1657                  * @return the document object, or null if XML has not been set
1658                  * @throws SAXException
1659                  * @throws IOException
1660                  * @throws ParserConfigurationException
1661                  */
1662                 public synchronized Document getDocument()
1663                                 throws ParserConfigurationException, IOException, SAXException {
1664                         if (xml == null) {
1665                                 return null;
1666                         }
1667
1668                         buildDocument();
1669                         return doc;
1670                 }
1671
1672                 /**
1673                  * Evaluates the specified XPath expression and returns a string result.
1674                  * This method throws exceptions on error.
1675                  * @param expression the expression
1676                  * @return the result object
1677                  * @throws ParserConfigurationException
1678                  * @throws IOException
1679                  * @throws SAXException
1680                  * @throws XPathExpressionException on error
1681                  */
1682                 public synchronized String evaluate(String expression)
1683                                 throws ParserConfigurationException, SAXException,
1684                                 IOException, XPathExpressionException {
1685                         return (String) evaluate(expression, XPathConstants.STRING);
1686                 }
1687
1688                 /**
1689                  * Evaluates the specified XPath expression.
1690                  * This method throws exceptions on error.
1691                  * @param expression the expression
1692                  * @param returnType the return type
1693                  * @return the result object
1694                  * @throws ParserConfigurationException
1695                  * @throws IOException
1696                  * @throws SAXException
1697                  * @throws XPathExpressionException on error
1698                  */
1699                 public synchronized Object evaluate(String expression, QName returnType)
1700                                 throws ParserConfigurationException, SAXException,
1701                                 IOException, XPathExpressionException {
1702
1703                         buildDocument();
1704                         XPathExpression expr = xpath.compile(expression);
1705                         return expr.evaluate(doc, returnType);
1706                 }
1707
1708                 /**
1709                  * Private helper method that builds the document object.
1710                  * Assumes the calling method is synchronized.
1711                  * @throws ParserConfigurationException
1712                  * @throws IOException
1713                  * @throws SAXException
1714                  */
1715                 private void buildDocument() throws ParserConfigurationException,
1716                                 IOException, SAXException {
1717                         if (doc == null) {
1718                                 if (xml == null) {
1719                                         throw new IOException("XML input is null");
1720                                 }
1721
1722                                 DocumentBuilder builder = factory.newDocumentBuilder();
1723                                 InputSource source = new InputSource(new StringReader(xml));
1724                                 doc = builder.parse(source);
1725                         }
1726                 }
1727         }
1728
1729         /**
1730          * A NamespaceContext class based on a Map.
1731          */
1732         private class SimpleNamespaceContext implements NamespaceContext {
1733                 private Map<String, String> prefixMap = new HashMap<String, String>();
1734                 private Map<String, String> uriMap = new HashMap<String, String>();
1735
1736                 public synchronized void add(String prefix, String uri) {
1737                         prefixMap.put(prefix, uri);
1738                         uriMap.put(uri, prefix);
1739                 }
1740
1741                 @Override
1742                 public synchronized String getNamespaceURI(String prefix) {
1743                         return prefixMap.get(prefix);
1744                 }
1745
1746                 @Override
1747                 public Iterator<String> getPrefixes(String uri) {
1748                         List<String> list = new ArrayList<String>();
1749                         String prefix = uriMap.get(uri);
1750                         if (prefix != null) {
1751                                 list.add(prefix);
1752                         }
1753                         return list.iterator();
1754                 }
1755
1756                 @Override
1757                 public String getPrefix(String uri) {
1758                         return uriMap.get(uri);
1759                 }
1760         }
1761
1762         /**
1763          * A VnfNotify XPathTool.
1764          */
1765         protected class VnfNotifyXPathTool extends XPathTool {
1766                 public VnfNotifyXPathTool() {
1767                         addNamespace("tns", "http://org.openecomp.mso/vnfNotify");
1768                 }
1769         }
1770
1771         /**
1772          * Helper class to make it easier to create this type.
1773          */
1774         private static class CreateVnfNotificationOutputs
1775                         extends org.openecomp.mso.bpmn.common.adapter.vnf.CreateVnfNotification.Outputs {
1776                 public void add(String key, String value) {
1777                         Entry entry = new Entry();
1778                         entry.setKey(key);
1779                         entry.setValue(value);
1780                         getEntry().add(entry);
1781                 }
1782         }
1783
1784         /**
1785          * Helper class to make it easier to create this type.
1786          */
1787         private static class UpdateVnfNotificationOutputs
1788                         extends org.openecomp.mso.bpmn.common.adapter.vnf.UpdateVnfNotification.Outputs {
1789                 public void add(String key, String value) {
1790                         Entry entry = new Entry();
1791                         entry.setKey(key);
1792                         entry.setValue(value);
1793                         getEntry().add(entry);
1794                 }
1795         }
1796 }