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