Containerization feature of SO
[so.git] / bpmn / MSOCommonBPMN / src / main / groovy / org / onap / so / bpmn / common / scripts / ExceptionUtil.groovy
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.so.bpmn.common.scripts
22
23 import static org.apache.commons.lang3.StringUtils.*
24
25 import com.google.common.xml.XmlEscapers
26
27 import org.apache.commons.lang3.*
28 import org.camunda.bpm.engine.delegate.BpmnError
29 import org.camunda.bpm.engine.delegate.DelegateExecution
30 import org.onap.so.bpmn.core.WorkflowException
31 import org.onap.so.logger.MessageEnum
32 import org.onap.so.logger.MsoLogger
33
34
35
36 /**
37  * @version 1.0
38  */
39 class ExceptionUtil extends AbstractServiceTaskProcessor {
40         private static final MsoLogger msoLogger = MsoLogger.getMsoLogger(MsoLogger.Catalog.BPEL, ExceptionUtil.class);
41
42
43
44         /**
45          * This error handling method maps an AAI Exception response to a
46          * WorkflowException Object.  It then sets the WorkflowException Object
47          * on the execution as "WorkflowException".
48          *
49          * This method formats the exception from AAI into the WorkflowException's
50          * errorMessage that CCD expects.
51          *
52          * @param execution the execution
53          * @param response the aai exception
54          */
55         WorkflowException MapAAIExceptionToWorkflowException(String response, DelegateExecution execution)
56         {
57                 def utils=new MsoUtils()
58                 def prefix=execution.getVariable("prefix")
59                 def errorMsg = execution.getVariable(prefix+"ErrorResponse")
60                 msoLogger.trace("Begin MapAAIExceptionToWorkflowException ")
61                 String text = null
62                 def variables
63                 String errorCode = '5000'
64                 WorkflowException wfex
65                 msoLogger.debug("response: " + response)
66                 try{
67                 try {
68                         //String msg = utils.getNodeXml(response, "Fault")
69                         variables = utils.getMultNodes(response, "variable")
70                         text = utils.getNodeText(response, "text")
71                 } catch (Exception ex) {
72                         //Ignore the exception - cases include non xml payload
73                         msoLogger.debug("error mapping error, ignoring: " + ex)
74                 }
75
76                 if(text != null) {
77                         if(variables.size()>=4){
78                                 text = text.replaceFirst("%1", variables[0])
79                                 text = text.replaceFirst("%2", variables[1])
80                                 text = text.replaceFirst("%3", variables[2])
81                                 text = text.replaceFirst("%4", variables[3])
82                         }
83                         String modifiedErrorMessage = 'Received error from A&AI (' + text +')'
84                         msoLogger.debug("ModifiedErrorMessage " + modifiedErrorMessage)
85                         // let $ModifiedErrorMessage := concat( 'Received error from A',$exceptionaai:ampersand,'AI (' ,functx:replace-multi($ErrorMessage,$from,$Variables ),')')
86                         buildWorkflowException(execution, 5000, modifiedErrorMessage)
87
88                         wfex = execution.getVariable("WorkflowException")
89                         msoLogger.error(MessageEnum.BPMN_GENERAL_EXCEPTION_ARG, "Fault", "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError, wfex.errorMessage);
90                         return wfex
91                 } else {
92                         try {
93                                 errorCode = MapErrorCode(errorMsg)
94                                 String mappedErrorMessage = MapErrorMessage(errorMsg, errorCode)
95
96                                 int errorCodeInt = Integer.parseInt(errorCode)
97                                 buildWorkflowException(execution, errorCodeInt, mappedErrorMessage)
98
99                                 msoLogger.debug("mappedErrorMessage " + mappedErrorMessage)
100                                 wfex = execution.getVariable("WorkflowException")
101                                 msoLogger.error(MessageEnum.BPMN_GENERAL_EXCEPTION_ARG, "Fault", "BPMN", MsoLogger.getServiceName(), MsoLogger.ErrorCode.UnknownError, wfex.errorMessage);
102                                 return wfex
103                         } catch(Exception ex) {
104                                 msoLogger.debug("error mapping error, return null: " + ex)
105                                 return null
106
107                         }
108                 }
109                 }catch(Exception e){
110                         msoLogger.debug("Exception occured during MapAAIExceptionToWorkflowException: " + e)
111                         buildWorkflowException(execution, 5000, "Error mapping AAI Response to WorkflowException")
112                 }
113         }
114
115         /**
116          * This error handling method maps an AAI Exception response to a
117          * WorkflowException Object.  It then sets the WorkflowException Object
118          * on the execution as "WorkflowException".
119          *
120          * This method takes the exact exception inside the <Fault> tags from AAI Response
121          * and puts it into the WorkflowException's errorMessage.
122          *
123          * @param execution the execution
124          * @param response the aai exception
125          */
126         WorkflowException MapAAIExceptionToWorkflowExceptionGeneric(DelegateExecution execution, String response, int resCode){
127                 def utils=new MsoUtils()
128                 msoLogger.debug("Start MapAAIExceptionToWorkflowExceptionGeneric Process")
129
130                 WorkflowException wfex
131                 try {
132                         if(utils.nodeExists(response, "Fault")){
133                                 String fault = utils.getNodeXml(response, "Fault")
134                                 fault = utils.removeXmlPreamble(fault)
135                                 fault = fault.replace("<Fault>", "").replace("</Fault>", "")
136                                 fault = fault.replaceAll("\\s+\\s+", "") // Removes extra white spaces
137                                 buildWorkflowException(execution, resCode, fault)
138                         }else if(utils.nodeExists(response, "RESTFault")){
139                                 String rFault = utils.getNodeXml(response, "RESTFault")
140                                 buildWorkflowException(execution, resCode, rFault)
141                         }else{
142                                 buildWorkflowException(execution, resCode, "Received a bad response from AAI")
143                         }
144                 } catch (Exception ex) {
145                         msoLogger.debug("Exception Occured during MapAAIExceptionToWorkflowExceptionGeneric: " + ex)
146                         buildWorkflowException(execution, resCode, "Internal Error - Occured in MapAAIExceptionToWorkflowExceptionGeneric")
147
148                 }
149                 msoLogger.debug("Outgoing WorkflowException is: " + execution.getVariable("WorkflowException"))
150                 msoLogger.debug("Completed MapAAIExceptionToWorkflowExceptionGeneric Process")
151         }
152
153         /**
154          * This method takes a WorkflowException Object and builds
155          * WorkflowException Xml. This method should only be used
156          * for the purpose of sending a sync error response or for
157          * creating a FalloutHandler request.
158          *
159          *@param - WorkflowException Object
160          *
161          *@return - String WorkflowException Xml
162          *
163          *
164          */
165         String buildErrorResponseXml(WorkflowException wfex) {
166                 String xml
167                 if(wfex != null){
168                         String mes = XmlEscapers.xmlContentEscaper().escape(wfex.getErrorMessage())
169                         int code = wfex.getErrorCode()
170                         xml =
171                         """<aetgt:WorkflowException xmlns:aetgt="http://org.onap/so/workflow/schema/v1">
172                                         <aetgt:ErrorMessage>${MsoUtils.xmlEscape(mes)}</aetgt:ErrorMessage>
173                                         <aetgt:ErrorCode>${MsoUtils.xmlEscape(code)}</aetgt:ErrorCode>
174                                   </aetgt:WorkflowException>"""
175
176                 }else{
177                         xml =
178                         """<aetgt:WorkflowException xmlns:aetgt="http://org.onap/so/workflow/schema/v1">
179                                         <aetgt:ErrorMessage>Internal Error</aetgt:ErrorMessage>
180                                         <aetgt:ErrorCode>2500</aetgt:ErrorCode>
181                                   </aetgt:WorkflowException>"""
182                 }
183                 return xml
184         }
185
186         /*
187         5000    Received error from A&AI ($A&AI_ERROR)  Asynchronous    During orchestration of the recipe, A&AI returned an error. The error returned by A&AI is passed through in $A&AI_ERROR.
188         5010    Could not communicate with A&AI Asynchronous    During orchestration of the recipe, a connection with A&AI could not be established.
189         5020    No response from A&AI   Asynchronous    During orchestration of the recipe, communication was established with A&AI, but no response was received within the configured timeout.
190         */
191         /**
192          *
193          * Utility Method for MapAAIExceptionToWorkflowException
194          *
195          *@param - String ErrorMessage
196          *
197          *@return - String ErrorCode
198          *
199          */
200         private String MapErrorCode(String errorMessage)
201         {
202                 if(errorMessage==null){
203                         return '5000'
204                 }
205                 errorMessage = errorMessage.toLowerCase();
206                 if(errorMessage.contains('timed out') || errorMessage.contains('timeout'))
207                         return '5020'
208                 else if (errorMessage.contains('connection'))
209                         return '5010'
210                 else
211                         return '5000'
212         }
213
214         /**
215          *
216          * Utility Method for MapAAIExceptionToWorkflowException
217          *
218          *@param - String ErrorMessage
219          *@param - String ErrorCode
220          *
221          *@return - String ErrorMessage
222          *
223          */
224         private String MapErrorMessage(String errorMessage, String errorCode)
225         {
226                 if(errorMessage == null){
227                         errorMessage=""
228                 }
229                 if( errorCode.equals('5010')){
230                                         return 'Could not communicate with A&AI'
231                 }else if (errorCode.equals('5020')){
232                         return 'No response from A&AI'
233                 }else{
234                         return 'Received error from A&AI (' +errorMessage +')'
235                 }
236         }
237
238         /**
239          *
240          * Utility Method for Mapping SDNC
241          * Adapter Response Codes
242          *
243          *@param - String sdncResponseCode
244          *
245          *@return - String code
246          *
247          */
248         String MapSDNCResponseCodeToErrorCode(String sdncResponseCode)
249         {
250                 if (sdncResponseCode == '500') {
251                         return '5310'
252                 } else if ( sdncResponseCode == '408') {
253                          return '5320'
254                 } else if ( sdncResponseCode == '60010') {
255                          return '5350'
256                 } else {
257                    return '5300'
258                 }
259         }
260
261         /**
262          * This error handling method builds a WorkflowException Object.  It sets it on
263          * the execution as "WorkflowException".
264          *
265          * @param execution the execution
266          * @param errorCode the error code
267          * @param errorMessage the error message
268          */
269         public void buildWorkflowException(DelegateExecution execution, int errorCode, String errorMessage) {
270                 MsoUtils utils = new MsoUtils()
271                 String processKey = getProcessKey(execution);
272                 msoLogger.debug("Building a WorkflowException for " + processKey)
273
274                 WorkflowException exception = new WorkflowException(processKey, errorCode, errorMessage);
275                 execution.setVariable("WorkflowException", exception);
276                 msoLogger.debug("Outgoing WorkflowException is " + exception)
277         }
278
279         /**
280          * This error handling method builds a WorkflowException Object and throws a
281          * MSOWorkflowException.  It throws a "MSOWorkflowException" BpmnError after
282          * setting the WorkflowException Object on the execution as "WorkflowException".
283          *
284          * @param execution the execution
285          * @param errorCode the error code
286          * @param errorMessage the error message
287          */
288         public void buildAndThrowWorkflowException(DelegateExecution execution, int errorCode, String errorMessage) {
289                 String processKey = getProcessKey(execution);
290                 msoLogger.debug("Building a WorkflowException for Subflow " + processKey)
291
292                 WorkflowException exception = new WorkflowException(processKey, errorCode, errorMessage);
293                 execution.setVariable("WorkflowException", exception);
294                 msoLogger.debug("Outgoing WorkflowException is " + exception)
295                 msoLogger.debug("Throwing MSOWorkflowException")
296                 throw new BpmnError("MSOWorkflowException")
297         }
298
299         /**
300          * This method is executed after an MSOWorkflowException is caught by a
301          * subflow (during subflows "Error Handling Sub Process").
302          * It ensures the WorkflowException variable is populated before ending the
303          * subflow and also logs the subflows outgoing WorkflowException Variable.
304          *
305          * @param - execution
306          *
307          */
308         public void processSubflowsBPMNException(DelegateExecution execution){
309                 String processKey = getProcessKey(execution)
310                 try{
311                         msoLogger.debug("Started ProcessSubflowsBPMNException Method")
312                         if(execution.getVariable("WorkflowException") == null){
313                                 buildWorkflowException(execution, 2500, "Internal Error - Occured During " + processKey)
314                         }
315
316                         msoLogger.debug(processKey + " Outgoing WorkflowException is: " + execution.getVariable("WorkflowException"))
317                 }catch(Exception e){
318                         msoLogger.debug("Caught Exception during ProcessSubflowsBPMNException Method: " + e)
319                 }
320                 msoLogger.debug("Completed ProcessSubflowsBPMNException Method")
321         }
322
323         /**
324          * This method is executed after an MSOWorkflowException is caught by a
325          * Mainflow. It builds and returns a FalloutHandler Request. It also
326          * verifies the WorkflowException variable is populated.
327          *
328          * @param - execution
329          * @param - requestInfo
330          *
331          * @return - falloutHandlerRequest
332          *
333          */
334         public String processMainflowsBPMNException(DelegateExecution execution, String requestInfo){
335                 String processKey = getProcessKey(execution)
336                 try{
337                         msoLogger.debug("Started ProcessMainflowBPMNException Method")
338                         if(execution.getVariable("WorkflowException") == null || isBlank(requestInfo)){
339                                 buildWorkflowException(execution, 2500, "Internal Error - WorkflowException Object and/or RequestInfo is null! " + processKey)
340                         }
341                         requestInfo = utils.removeXmlPreamble(requestInfo)
342                         WorkflowException wfex = execution.getVariable("WorkflowException")
343                         String errorMessage = XmlEscapers.xmlContentEscaper().escape(wfex.getErrorMessage())
344                         int errorCode = wfex.getErrorCode()
345
346                         String falloutHandlerRequest =
347                         """<aetgt:FalloutHandlerRequest xmlns:aetgt="http://org.onap/so/workflow/schema/v1"
348                                                                      xmlns:ns="http://org.onap/so/request/types/v1"
349                                                                      xmlns:wfsch="http://org.onap/so/workflow/schema/v1">
350                                            ${requestInfo}
351                                                 <aetgt:WorkflowException xmlns:aetgt="http://org.onap/so/workflow/schema/v1">
352                                                         <aetgt:ErrorMessage>${MsoUtils.xmlEscape(errorMessage)}</aetgt:ErrorMessage>
353                                                         <aetgt:ErrorCode>${MsoUtils.xmlEscape(errorCode)}</aetgt:ErrorCode>
354                                                 </aetgt:WorkflowException>
355                                         </aetgt:FalloutHandlerRequest>"""
356
357                         msoLogger.debug(processKey + " Outgoing WorkflowException is: " + execution.getVariable("WorkflowException"))
358                         msoLogger.debug(processKey + " Outgoing FalloutHandler Request is: " + falloutHandlerRequest)
359
360                         return falloutHandlerRequest
361
362                 }catch(Exception e){
363                         msoLogger.debug("Caught Exception during ProcessMainflowBPMNException Method: " + e)
364                         return null
365                 }
366                 msoLogger.debug("Completed ProcessMainflowBPMNException Method")
367         }
368
369         /**
370          *
371          * This method is executed after an Java Exception is caught
372          * It sets the WorkflowException variable. The method can be used in either mainflow or subflows.
373          *
374          * @param - execution
375          *
376          */
377         public void processJavaException(DelegateExecution execution){
378                 String processKey = getProcessKey(execution)
379                 try{
380                         msoLogger.debug("Caught a Java Exception in " + processKey)
381                         msoLogger.debug("Started processJavaException Method")
382                         // if the BPMN flow java error handler sets "BPMN_javaExpMsg", append it to the WFE
383                         String javaExpMsg = execution.getVariable("BPMN_javaExpMsg")
384                         String wfeExpMsg = "Catch a Java Lang Exception in " + processKey
385                         if (javaExpMsg != null && !javaExpMsg.empty) {
386                                 wfeExpMsg = wfeExpMsg + ": " + javaExpMsg
387                         }
388                         buildWorkflowException(execution, 2500, wfeExpMsg)
389
390                 }catch(BpmnError b){
391                         throw b
392                 }catch(Exception e){
393                         msoLogger.debug("Caught Exception during processJavaException Method: " + e)
394                         buildWorkflowException(execution, 2500, "Internal Error - During Process Java Exception")
395                 }
396                 msoLogger.debug("Completed processJavaException Method")
397         }
398
399
400         public void preProcessRequest(DelegateExecution execution) {
401                 // TODO Auto-generated method stub
402
403         }
404         
405         public String getErrorMessage(WorkflowException wfe, String processKey) {
406                 if(wfe == null) {
407                         return "Unexpected error encountered in " + processKey
408                 }
409                 else {
410                         return wfe.getErrorMessage()
411                 }
412         }
413         
414         public int getErrorCode(WorkflowException wfe) {
415                 if(wfe == null) {
416                         return 2500
417                 }
418                 else {
419                         return wfe.getErrorCode()
420                 }
421         }
422 }