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