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