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