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