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