Merge "Sonar fix too many method param"
[so.git] / bpmn / MSOCommonBPMN / src / main / groovy / org / onap / so / bpmn / common / scripts / VnfAdapterRestV1.groovy
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Modifications Copyright (c) 2019 Samsung
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.so.bpmn.common.scripts
24
25 import org.onap.so.logger.LoggingAnchor
26 import org.onap.so.client.HttpClientFactory
27 import org.onap.so.logger.ErrorCode
28
29 import javax.ws.rs.core.Response
30 import org.apache.commons.lang3.*
31 import org.camunda.bpm.engine.delegate.BpmnError
32 import org.camunda.bpm.engine.delegate.DelegateExecution
33 import org.onap.so.bpmn.core.UrnPropertiesReader
34 import org.onap.so.client.HttpClient
35 import org.onap.so.logger.MessageEnum
36 import org.slf4j.Logger
37 import org.slf4j.LoggerFactory
38 import org.onap.so.utils.TargetEntity
39 import java.util.UUID
40
41
42
43
44 class VnfAdapterRestV1 extends AbstractServiceTaskProcessor {
45     private static final Logger logger = LoggerFactory.getLogger( VnfAdapterRestV1.class);
46
47
48         ExceptionUtil exceptionUtil = new ExceptionUtil()
49
50         // VNF Response Processing
51         public void preProcessRequest (DelegateExecution execution) {
52                 def method = getClass().getSimpleName() + '.preProcessRequest(' +
53                         'execution=' + execution.getId() +
54                         ')'
55                 logger.trace('Entered ' + method)
56
57                 def prefix="VNFREST_"
58                 execution.setVariable("prefix", prefix)
59                 setSuccessIndicator(execution, false)
60
61                 try {
62                         String request = validateRequest(execution, "mso-request-id")
63
64                         // Get the request type (the name of the root element) from the request
65
66                         Node root = new XmlParser().parseText(request)
67                         String requestType = root.name()
68                         execution.setVariable(prefix + 'requestType', requestType)
69                         logger.debug(getProcessKey(execution) + ': ' + prefix + 'requestType = ' + requestType)
70
71                         logger.debug('VnfAdapterRestV1, request: ' + request)
72                         // Get the messageId from the request
73
74                         String messageId = getChildText(root, 'messageId')
75
76                         if ('rollbackVolumeGroupRequest'.equals(requestType)) {
77                                 messageId = getMessageIdForVolumeGroupRollback(root)
78                         }
79
80                         if (messageId == null || messageId.isEmpty()) {
81                                 String msg = getProcessKey(execution) + ': no messageId in ' + requestType
82                                 logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
83                                                 ErrorCode.UnknownError.getValue());
84                                 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
85                         }
86
87                         execution.setVariable('VNFAResponse_CORRELATOR', messageId)
88                         logger.debug(getProcessKey(execution) + ': VNFAResponse_CORRELATOR = ' + messageId)
89
90                         // Get the notificationUrl from the request
91
92                         String notificationUrl = getChildText(root, 'notificationUrl')
93
94                         if (notificationUrl == null || notificationUrl.isEmpty()) {
95                                 String msg = getProcessKey(execution) + ': no notificationUrl in ' + requestType
96                                 logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
97                                                 ErrorCode.UnknownError.getValue());
98                                 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
99                         }
100
101                         execution.setVariable(prefix + 'notificationUrl', notificationUrl)
102                         logger.debug(getProcessKey(execution) + ': ' + prefix + 'notificationUrl = ' + notificationUrl)
103
104                         // Determine the VnfAdapter endpoint
105
106                         String vnfAdapterEndpoint = UrnPropertiesReader.getVariable("mso.adapters.vnf.rest.endpoint", execution)
107
108                         if (vnfAdapterEndpoint == null || vnfAdapterEndpoint.isEmpty()) {
109                                 String msg = getProcessKey(execution) + ': mso:adapters:vnf:rest:endpoint URN mapping is not defined'
110                                 logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
111                                                 ErrorCode.UnknownError.getValue());
112                                 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
113                         }
114
115                         while (vnfAdapterEndpoint.endsWith('/')) {
116                                 vnfAdapterEndpoint = vnfAdapterEndpoint.substring(0, vnfAdapterEndpoint.length()-1)
117                         }
118
119                         String vnfAdapterMethod = null
120                         String vnfAdapterUrl = null
121                         String vnfAdapterRequest = request
122
123                         if ('createVfModuleRequest'.equals(requestType)) {
124                                 String vnfId = getChildText(root, 'vnfId')
125
126                                 if (vnfId == null || vnfId.isEmpty()) {
127                                         String msg = getProcessKey(execution) + ': no vnfId in ' + requestType
128                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
129                                                         ErrorCode.UnknownError.getValue());
130                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
131                                 }
132
133                                 vnfAdapterMethod = 'POST'
134                                 vnfAdapterUrl = vnfAdapterEndpoint + '/' + URLEncoder.encode(vnfId, 'UTF-8') + '/vf-modules'
135
136                         } else if ('updateVfModuleRequest'.equals(requestType)) {
137                                 String vnfId = getChildText(root, 'vnfId')
138
139                                 if (vnfId == null || vnfId.isEmpty()) {
140                                         String msg = getProcessKey(execution) + ': no vnfId in ' + requestType
141                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
142                                                         ErrorCode.UnknownError.getValue());
143                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
144                                 }
145
146                                 String vfModuleId = getChildText(root, 'vfModuleId')
147
148                                 if (vfModuleId == null || vfModuleId.isEmpty()) {
149                                         String msg = getProcessKey(execution) + ': no vfModuleId in ' + requestType
150                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
151                                                         ErrorCode.UnknownError.getValue());
152                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
153                                 }
154
155                                 vnfAdapterMethod = 'PUT'
156                                 vnfAdapterUrl = vnfAdapterEndpoint + '/' + URLEncoder.encode(vnfId, 'UTF-8') +
157                                         '/vf-modules/' + URLEncoder.encode(vfModuleId, 'UTF-8')
158
159                         } else if ('deleteVfModuleRequest'.equals(requestType)) {
160                                 String vnfId = getChildText(root, 'vnfId')
161
162                                 if (vnfId == null || vnfId.isEmpty()) {
163                                         String msg = getProcessKey(execution) + ': no vnfId in ' + requestType
164                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
165                                                         ErrorCode.UnknownError.getValue());
166                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
167                                 }
168
169                                 String vfModuleId = getChildText(root, 'vfModuleId')
170
171                                 if (vfModuleId == null || vfModuleId.isEmpty()) {
172                                         String msg = getProcessKey(execution) + ': no vfModuleId in ' + requestType
173                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
174                                                         ErrorCode.UnknownError.getValue());
175                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
176                                 }
177
178                                 vnfAdapterMethod = 'DELETE'
179                                 vnfAdapterUrl = vnfAdapterEndpoint + '/' + URLEncoder.encode(vnfId, 'UTF-8') +
180                                         '/vf-modules/' + URLEncoder.encode(vfModuleId, 'UTF-8')
181
182                         } else if ('rollbackVfModuleRequest'.equals(requestType)) {
183                                 Node vfModuleRollbackNode = getChild(root, 'vfModuleRollback')
184
185                                 if (vfModuleRollbackNode == null) {
186                                         String msg = getProcessKey(execution) + ': no vfModuleRollback in ' + requestType
187                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
188                                                         ErrorCode.UnknownError.getValue());
189                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
190                                 }
191
192                                 String vnfId = getChildText(vfModuleRollbackNode, 'vnfId')
193
194                                 if (vnfId == null || vnfId.isEmpty()) {
195                                         String msg = getProcessKey(execution) + ': no vnfId in ' + requestType
196                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
197                                                         ErrorCode.UnknownError.getValue());
198                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
199                                 }
200
201                                 String vfModuleId = getChildText(vfModuleRollbackNode, 'vfModuleId')
202
203                                 if (vfModuleId == null || vfModuleId.isEmpty()) {
204                                         String msg = getProcessKey(execution) + ': no vfModuleId in ' + requestType
205                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
206                                                         ErrorCode.UnknownError.getValue());
207                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
208                                 }
209
210                                 vnfAdapterMethod = 'DELETE'
211                                 vnfAdapterUrl = vnfAdapterEndpoint + '/' + URLEncoder.encode(vnfId, 'UTF-8') +
212                                         '/vf-modules/' + URLEncoder.encode(vfModuleId, 'UTF-8') + '/rollback'
213
214                         } else if ('createVolumeGroupRequest'.equals(requestType)) {
215                                 vnfAdapterMethod = 'POST'
216                                 if (vnfAdapterEndpoint.endsWith('v1/vnfs')) {
217                                         vnfAdapterEndpoint = vnfAdapterEndpoint.substring(0, (vnfAdapterEndpoint.length()-'/vnfs'.length()))
218                                 }
219                                 vnfAdapterUrl = vnfAdapterEndpoint + '/volume-groups'
220
221                         } else if ('updateVolumeGroupRequest'.equals(requestType)) {
222                                 String volumeGroupId = getChildText(root, 'volumeGroupId')
223
224                                 if (volumeGroupId == null || volumeGroupId.isEmpty()) {
225                                         String msg = getProcessKey(execution) + ': no volumeGroupId in ' + requestType
226                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
227                                                         ErrorCode.UnknownError.getValue());
228                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
229                                 }
230
231                                 vnfAdapterMethod = 'PUT'
232                                 if (vnfAdapterEndpoint.endsWith('v1/vnfs')) {
233                                         vnfAdapterEndpoint = vnfAdapterEndpoint.substring(0, (vnfAdapterEndpoint.length()-'/vnfs'.length()))
234                                 }
235                                 vnfAdapterUrl = vnfAdapterEndpoint + '/volume-groups/' + URLEncoder.encode(volumeGroupId, 'UTF-8')
236
237                         } else if ('deleteVolumeGroupRequest'.equals(requestType)) {
238                                 String volumeGroupId = getChildText(root, 'volumeGroupId')
239
240                                 if (volumeGroupId == null || volumeGroupId.isEmpty()) {
241                                         String msg = getProcessKey(execution) + ': no volumeGroupId in ' + requestType
242                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
243                                                         ErrorCode.UnknownError.getValue());
244                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
245                                 }
246
247                                 vnfAdapterMethod = 'DELETE'
248                                 if (vnfAdapterEndpoint.endsWith('v1/vnfs')) {
249                                         vnfAdapterEndpoint = vnfAdapterEndpoint.substring(0, (vnfAdapterEndpoint.length()-'/vnfs'.length()))
250                                 }
251                                 vnfAdapterUrl = vnfAdapterEndpoint + '/volume-groups/' + URLEncoder.encode(volumeGroupId, 'UTF-8')
252
253                         } else if ('rollbackVolumeGroupRequest'.equals(requestType)) {
254                                 String volumeGroupId = getVolumeGroupIdFromRollbackRequest(root)
255
256                                 if (volumeGroupId == null || volumeGroupId.isEmpty()) {
257                                         String msg = getProcessKey(execution) + ': no volumeGroupId in ' + requestType
258                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
259                                                         ErrorCode.UnknownError.getValue());
260                                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
261                                 }
262
263                                 vnfAdapterMethod = 'DELETE'
264                                 if (vnfAdapterEndpoint.endsWith('v1/vnfs')) {
265                                         vnfAdapterEndpoint = vnfAdapterEndpoint.substring(0, (vnfAdapterEndpoint.length()-'/vnfs'.length()))
266                                 }
267                                 vnfAdapterUrl = vnfAdapterEndpoint + '/volume-groups/' + URLEncoder.encode(volumeGroupId, 'UTF-8')  + '/rollback'
268
269                         } else {
270                                 String msg = getProcessKey(execution) + ': Unsupported request type: ' + requestType
271                                 logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
272                                                 ErrorCode.UnknownError.getValue());
273                                 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
274                         }
275
276                         execution.setVariable(prefix + 'vnfAdapterMethod', vnfAdapterMethod)
277                         logger.debug(getProcessKey(execution) + ': ' + prefix + 'vnfAdapterMethod = ' + vnfAdapterMethod)
278                         execution.setVariable(prefix + 'vnfAdapterUrl', vnfAdapterUrl)
279                         logger.debug(getProcessKey(execution) + ': ' + prefix + 'vnfAdapterUrl = ' + vnfAdapterUrl)
280                         execution.setVariable(prefix + 'vnfAdapterRequest', vnfAdapterRequest)
281                         logger.debug(getProcessKey(execution) + ': ' + prefix + 'vnfAdapterRequest = \n' + vnfAdapterRequest)
282
283                         // Get the Basic Auth credentials for the VnfAdapter
284
285                         String basicAuthValue = UrnPropertiesReader.getVariable("mso.adapters.po.auth", execution)
286
287                         if (basicAuthValue == null || basicAuthValue.isEmpty()) {
288                                 logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(),
289                                                 getProcessKey(execution) + ": mso:adapters:po:auth URN mapping is not defined", "BPMN",
290                                                 ErrorCode.UnknownError.getValue());
291                         } else {
292                                 try {
293                                         def encodedString = utils.getBasicAuth(basicAuthValue, UrnPropertiesReader.getVariable("mso.msoKey", execution))
294                                         execution.setVariable(prefix + 'basicAuthHeaderValue', encodedString)
295                                 } catch (IOException ex) {
296                                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(),
297                                                         getProcessKey(execution) + ": Unable to encode BasicAuth credentials for VnfAdapter",
298                                                         "BPMN", ErrorCode.UnknownError.getValue(), ex);
299                                 }
300                         }
301
302                 } catch (BpmnError e) {
303                         logger.debug(" Rethrowing MSOWorkflowException")
304                         throw e
305                 } catch (Exception e) {
306                         String msg = 'Caught exception in ' + method + ": " + e
307                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
308                                         ErrorCode.UnknownError.getValue());
309                         logger.debug(msg)
310                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
311                 }
312         }
313
314         public String getVolumeGroupIdFromRollbackRequest(Node root) {
315                 return root.'volumeGroupRollback'.'volumeGroupId'.text()
316         }
317
318         public String getMessageIdForVolumeGroupRollback(Node root) {
319                 return root.'volumeGroupRollback'.'messageId'.text()
320         }
321
322         /**
323          * This method is used instead of an HTTP Connector task because the
324          * connector does not allow DELETE with a body.
325          */
326         public void sendRequestToVnfAdapter(DelegateExecution execution) {
327                 def method = getClass().getSimpleName() + '.sendRequestToVnfAdapter(' +
328                         'execution=' + execution.getId() +
329                         ')'
330                 logger.trace('Entered ' + method)
331
332                 String prefix = execution.getVariable('prefix')
333
334                 try {
335                         String vnfAdapterMethod = execution.getVariable(prefix + 'vnfAdapterMethod')
336                         String vnfAdapterUrl = execution.getVariable(prefix + 'vnfAdapterUrl')
337                         String vnfAdapterRequest = execution.getVariable(prefix + 'vnfAdapterRequest')
338
339                         URL url = new URL(vnfAdapterUrl);
340
341                         HttpClient httpClient = new HttpClientFactory().newXmlClient(url, TargetEntity.VNF_ADAPTER)
342                         httpClient.addAdditionalHeader("Authorization", execution.getVariable(prefix + "basicAuthHeaderValue"))
343                         
344                         httpClient.addAdditionalHeader("X-ONAP-RequestID", execution.getVariable("mso-request-id"))
345                         httpClient.addAdditionalHeader("X-ONAP-InvocationID", UUID.randomUUID().toString())
346                         httpClient.addAdditionalHeader("X-ONAP-PartnerName", "SO-VNFAdapter")
347                         Response response;
348
349                         if ("GET".equals(vnfAdapterMethod)) {
350                                 response = httpClient.get()
351                         } else if ("PUT".equals(vnfAdapterMethod)) {
352                                 response = httpClient.put(vnfAdapterRequest)
353                         } else if ("POST".equals(vnfAdapterMethod)) {
354                                 response = httpClient.post(vnfAdapterRequest)
355                         } else if ("DELETE".equals(vnfAdapterMethod)) {
356                                 response = httpClient.delete(vnfAdapterRequest)
357                         } else {
358                                 String msg = 'Unsupported HTTP method "' + vnfAdapterMethod + '" in ' + method + ": " + e
359                                 logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
360                                                 ErrorCode.UnknownError.getValue());
361                                 exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
362                         }
363
364                         execution.setVariable(prefix + "vnfAdapterStatusCode", response.getStatus())
365                         if(response.hasEntity()){
366                                 execution.setVariable(prefix + "vnfAdapterResponse", response.readEntity(String.class))
367                         }
368                 } catch (BpmnError e) {
369                         throw e
370                 } catch (Exception e) {
371                         String msg = 'Caught exception in ' + method + ": " + e
372                         logger.error(LoggingAnchor.FOUR, MessageEnum.BPMN_GENERAL_EXCEPTION_ARG.toString(), msg, "BPMN",
373                                         ErrorCode.UnknownError.getValue());
374                         exceptionUtil.buildAndThrowWorkflowException(execution, 2000, msg)
375                 }
376         }
377
378         public void processCallback(DelegateExecution execution){
379                 def method = getClass().getSimpleName() + '.processCallback(' +
380                         'execution=' + execution.getId() +
381                         ')'
382                 logger.trace('Entered ' + method)
383
384                 String callback = execution.getVariable('VNFAResponse_MESSAGE')
385
386                 try {
387                         logger.debug(getProcessKey(execution) + ": received callback:\n" + callback)
388
389                         // The XML callback is available to the calling flow in any case,
390                         // even if a WorkflowException is generated.
391                         execution.setVariable(getProcessKey(execution) + 'Response', callback)
392                         // TODO: Should deprecate use of processKey+Response variable for the response. Will use "WorkflowResponse" instead.
393                         execution.setVariable("WorkflowResponse", callback)
394
395                         callback = utils.removeXmlPreamble(callback)
396
397                         Node root = new XmlParser().parseText(callback)
398                         if (root.name().endsWith('Exception')) {
399                                 vnfAdapterWorkflowException(execution, callback)
400                         }
401                 } catch (Exception e) {
402                         logger.debug("Error encountered within VnfAdapterRest ProcessCallback method: {}", e.getMessage(), e)
403                         exceptionUtil.buildAndThrowWorkflowException(execution, 7020, "Error encountered within VnfAdapterRest ProcessCallback method")
404                 }
405         }
406
407         /**
408          * Tries to parse the response as XML to extract the information to create
409          * a WorkflowException.  If the response cannot be parsed, a more generic
410          * WorkflowException is created.
411          */
412         public void vnfAdapterWorkflowException(DelegateExecution execution, Object response) {
413                 try {
414                         Node root = new XmlParser().parseText(response)
415                         String category = getChildText(root, "category")
416                         category = category == null || category.isEmpty() ? "" : " category='" + category + "'"
417                         String message = getChildText(root, "message")
418                         message = message == null || message.isEmpty() ? "" : " message='" + message + "'"
419                         String rolledBack = getChildText(root, "rolledBack")
420                         rolledBack = rolledBack == null || rolledBack.isEmpty() ? "" : " rolledBack='" + rolledBack + "'"
421                         exceptionUtil.buildWorkflowException(execution, 7020, "Received " + root.name() +
422                                 " from VnfAdapter:" + category + message + rolledBack);
423                 } catch (Exception e) {
424                         response = response == null || String.valueOf(response).isEmpty() ? "NONE" : response
425                         exceptionUtil.buildWorkflowException(execution, 7020, "Received error from VnfAdapter: " + response)
426                 }
427         }
428
429         /**
430          * Gets the named child of the specified node.
431          * @param node the node
432          * @param name the child name
433          * @return the child node, or null if no such child exists
434          */
435         private Node getChild(Node node, String name) {
436                 for (Node child : node.children()) {
437                         if (child.name() == name) {
438                                 return child
439                         }
440                 }
441                 return null
442         }
443
444         /**
445          * Gets the text of the named child of the specified node.
446          * @param node the node
447          * @param name the child name
448          * @return the child node text, or null if no such child exists
449          */
450         private String getChildText(Node node, String name) {
451                 Node child = getChild(node, name)
452                 return child == null ? null : child.text()
453         }
454         
455         public Logger getLogger() {
456                 return logger;
457         }
458 }